diff --git a/CHANGES.md b/CHANGES.md index 3fa61de086..42c4b8ae4a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -63,6 +63,10 @@ Features * [#689](https://github.com/java-native-access/jna/pull/689): Add `GetProcAddress(HMODULE, int)` to `com.sun.jna.platform.win32.Kernel32` - [@matthiasblaesing](https://github.com/matthiasblaesing). * [#723](https://github.com/java-native-access/jna/pull/723): Added `com.sun.jna.platform.win32.Wevtapi` and `com.sun.jna.platform.win32.Winevt` - [@sakamotodesu](https://github.com/sakamotodesu). * [#720](https://github.com/java-native-access/jna/issues/720): Added `SetThreadExecutionState` to `com.sun.jna.platform.win32.Kernel32` - [@matthiasblaesing](https://github.com/matthiasblaesing). +* [#732](https://github.com/java-native-access/jna/pull/732): Added `com.sun.jna.platform.win32.WinioctlUtil` for help in determining FSCTL_* codes - [@amarcionek](https://github.com/amarcionek). +* [#732](https://github.com/java-native-access/jna/pull/732): Added `com.sun.jna.platform.win32.Ntifs` with Reparse Point structures and defines - [@amarcionek](https://github.com/amarcionek). +* [#732](https://github.com/java-native-access/jna/pull/732): Added initialization of FILETIME from LARGE_INTEGER - [@amarcionek](https://github.com/amarcionek). +* [#732](https://github.com/java-native-access/jna/pull/732): Added `GetFileInformationByHandleEx` and `SetFileInformationByHandle` to `com.sun.jna.platform.win32.Kernel32` - [@amarcionek](https://github.com/amarcionek). Bug Fixes --------- diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java index 7715451120..ecb23c5120 100755 --- a/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Advapi32Util.java @@ -41,6 +41,7 @@ import static com.sun.jna.platform.win32.WinNT.SE_DACL_PROTECTED; import static com.sun.jna.platform.win32.WinNT.SE_SACL_PROTECTED; import static com.sun.jna.platform.win32.WinNT.STANDARD_RIGHTS_READ; +import static com.sun.jna.platform.win32.WinNT.TOKEN_ADJUST_PRIVILEGES; import static com.sun.jna.platform.win32.WinNT.TOKEN_DUPLICATE; import static com.sun.jna.platform.win32.WinNT.TOKEN_IMPERSONATE; import static com.sun.jna.platform.win32.WinNT.TOKEN_QUERY; @@ -48,6 +49,7 @@ import static com.sun.jna.platform.win32.WinNT.UNPROTECTED_SACL_SECURITY_INFORMATION; import java.io.ByteArrayOutputStream; +import java.io.Closeable; import java.io.File; import java.io.IOException; import java.util.ArrayList; @@ -81,6 +83,7 @@ import com.sun.jna.platform.win32.WinNT.SECURITY_IMPERSONATION_LEVEL; import com.sun.jna.platform.win32.WinNT.SID_AND_ATTRIBUTES; import com.sun.jna.platform.win32.WinNT.SID_NAME_USE; +import com.sun.jna.platform.win32.WinNT.TOKEN_TYPE; import com.sun.jna.platform.win32.WinReg.HKEY; import com.sun.jna.platform.win32.WinReg.HKEYByReference; import com.sun.jna.ptr.IntByReference; @@ -2636,4 +2639,207 @@ public DWORD callback(Pointer pbData, Pointer pvCallbackContext, // close Advapi32.INSTANCE.CloseEncryptedFileRaw(pvContext.getValue()); } + + /** + * Convenience class to enable certain Windows process privileges + */ + public static class Privilege implements Closeable { + /** + * If true, the thread is currently impersonating + */ + private boolean currentlyImpersonating = false; + + /** + * If true, the privileges have been enabled + */ + private boolean privilegesEnabled = false; + + /** + * LUID form of the privileges + */ + private final WinNT.LUID[] pLuids; + + /** + * Construct and enable a set of privileges + * @param privileges the names of the privileges in the form of SE_* from Advapi32.java + * @throws IllegalArgumentException + */ + public Privilege(String... privileges) throws IllegalArgumentException, Win32Exception { + pLuids = new WinNT.LUID[privileges.length]; + int i = 0; + for (String p : privileges) { + pLuids[i] = new WinNT.LUID(); + if (!Advapi32.INSTANCE.LookupPrivilegeValue(null, p, pLuids[i])) { + throw new IllegalArgumentException("Failed to find privilege \"" + privileges[i] + "\" - " + Kernel32.INSTANCE.GetLastError()); + } + i++; + } + } + + /** + * Calls disable() to remove the privileges + * @see java.io.Closeable#close() + */ + @Override + public void close() { + this.disable(); + } + + /** + * Enables the given privileges. If required, it will duplicate the process token. No resources are left open when this completes. That is, it is + * NOT required to drop the privileges, although it is considered a best practice if you do not need it. This class is state full. It keeps track + * of whether it has enabled the privileges. Multiple calls to enable() without a drop() in between have no affect. + * @return pointer to self (Privilege) as a convenience for try with resources statements + * @throws Win32Exception + */ + public Privilege enable() throws Win32Exception { + // Ignore if already enabled. + if (privilegesEnabled) + return this; + + // Get thread token + final HANDLEByReference phThreadToken = new HANDLEByReference(); + + try { + phThreadToken.setValue(getThreadToken()); + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(pLuids.length); + for (int i = 0; i < pLuids.length; i++) { + tp.Privileges[i] = new WinNT.LUID_AND_ATTRIBUTES(pLuids[i], new DWORD(WinNT.SE_PRIVILEGE_ENABLED)); + } + if (!Advapi32.INSTANCE.AdjustTokenPrivileges(phThreadToken.getValue(), false, tp, 0, null, null)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + privilegesEnabled = true; + } + catch (Win32Exception ex) { + // If fails, clean up + if (currentlyImpersonating) { + Advapi32.INSTANCE.SetThreadToken(null, null); + currentlyImpersonating = false; + } + else { + if (privilegesEnabled) { + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(pLuids.length); + for (int i = 0; i < pLuids.length; i++) { + tp.Privileges[i] = new WinNT.LUID_AND_ATTRIBUTES(pLuids[i], new DWORD(0)); + } + Advapi32.INSTANCE.AdjustTokenPrivileges(phThreadToken.getValue(), false, tp, 0, null, null); + privilegesEnabled = false; + } + } + throw ex; + } + finally { + // Always close the thread token + if ((phThreadToken.getValue() != WinBase.INVALID_HANDLE_VALUE) + && (phThreadToken.getValue() != null)) { + Kernel32.INSTANCE.CloseHandle(phThreadToken.getValue()); + phThreadToken.setValue(null); + } + } + return this; + } + + /** + * Disabled the prior enabled privilege + * @throws Win32Exception + */ + public void disable() throws Win32Exception { + // Get thread token + final HANDLEByReference phThreadToken = new HANDLEByReference(); + + try { + phThreadToken.setValue(getThreadToken()); + if (currentlyImpersonating) { + Advapi32.INSTANCE.SetThreadToken(null, null); + } + else + { + if (privilegesEnabled) { + WinNT.TOKEN_PRIVILEGES tp = new WinNT.TOKEN_PRIVILEGES(pLuids.length); + for (int i = 0; i < pLuids.length; i++) { + tp.Privileges[i] = new WinNT.LUID_AND_ATTRIBUTES(pLuids[i], new DWORD(0)); + } + Advapi32.INSTANCE.AdjustTokenPrivileges(phThreadToken.getValue(), false, tp, 0, null, null); + privilegesEnabled = false; + } + } + } + finally { + // Close the thread token + if ((phThreadToken.getValue() != WinBase.INVALID_HANDLE_VALUE) + && (phThreadToken.getValue() != null)) { + Kernel32.INSTANCE.CloseHandle(phThreadToken.getValue()); + phThreadToken.setValue(null); + } + } + } + + /** + * Get a handle to the thread token. May duplicate the process token + * and set as the thread token if ther thread has no token. + * @return HANDLE to the thread token + * @throws Win32Exception + */ + private HANDLE getThreadToken() throws Win32Exception { + // we need to create a new token here for the duplicate + final HANDLEByReference phThreadToken = new HANDLEByReference(); + final HANDLEByReference phProcessToken = new HANDLEByReference(); + + try { + // open thread token + if (!Advapi32.INSTANCE.OpenThreadToken(Kernel32.INSTANCE.GetCurrentThread(), + TOKEN_ADJUST_PRIVILEGES, + false, + phThreadToken)) { + // OpenThreadToken may fail with W32Errors.ERROR_NO_TOKEN if current thread is anonymous. Check for that condition here. If not, throw an error. + int lastError = Kernel32.INSTANCE.GetLastError(); + if (W32Errors.ERROR_NO_TOKEN != lastError) { + throw new Win32Exception(lastError); + } + + // Due to ERROR_NO_TOKEN, we need to open the process token to duplicate it, then set our thread token. + if (!Advapi32.INSTANCE.OpenProcessToken(Kernel32.INSTANCE.GetCurrentProcess(), TOKEN_DUPLICATE, phProcessToken)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + // Process token opened, now duplicate + if (!Advapi32.INSTANCE.DuplicateTokenEx(phProcessToken.getValue(), + TOKEN_ADJUST_PRIVILEGES | TOKEN_IMPERSONATE, + null, + SECURITY_IMPERSONATION_LEVEL.SecurityImpersonation, + TOKEN_TYPE.TokenImpersonation, + phThreadToken)) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + + // And set thread token. + if (!Advapi32.INSTANCE.SetThreadToken(null, phThreadToken.getValue())) { + throw new Win32Exception(Kernel32.INSTANCE.GetLastError()); + } + currentlyImpersonating = true; + } + } + catch (Win32Exception ex) { + // Close the thread token + if ((phThreadToken.getValue() != WinBase.INVALID_HANDLE_VALUE) + && (phThreadToken.getValue() != null)) { + Kernel32.INSTANCE.CloseHandle(phThreadToken.getValue()); + phThreadToken.setValue(null); + } + throw ex; + } + finally + { + // Always close the process token + if ((phProcessToken.getValue() != WinBase.INVALID_HANDLE_VALUE) + && (phProcessToken.getValue() != null)) { + Kernel32.INSTANCE.CloseHandle(phProcessToken.getValue()); + phProcessToken.setValue(null); + } + } + + return phThreadToken.getValue(); + } + } } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java index fcca23ec1a..f66595f050 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Kernel32.java @@ -1443,6 +1443,48 @@ boolean GetLogicalProcessorInformation(Pointer buffer, */ boolean GlobalMemoryStatusEx(MEMORYSTATUSEX lpBuffer); + /** + * Retrieves file information for the specified file. + * To set file information using a file handle, see SetFileInformationByHandle. + * @param hFile + * A handle to the file that contains the information to be retrieved. + * @param FileInformationClass + * A FILE_INFO_BY_HANDLE_CLASS enumeration value that specifies the type of + * information to be retrieved. + * @param lpFileInformation + * A pointer to the buffer that receives the requested file information. + * The structure that is returned corresponds to the class that is specified + * by FileInformationClass. + * @param dwBufferSize + * The size of the lpFileInformation buffer, in bytes. + * @return If the function succeeds, the return value is nonzero and file information + * data is contained in the buffer pointed to by the lpFileInformation parameter. + * If the function fails, the return value is zero. To get extended error + * information, call GetLastError. + */ + boolean GetFileInformationByHandleEx(HANDLE hFile, int FileInformationClass, Pointer lpFileInformation, DWORD dwBufferSize); + + /** + * Sets the file information for the specified file. To retrieve file information using a + * file handle, see GetFileInformationByHandleEx. + * @param hFile + * A handle to the file for which to change information. + * This handle must be opened with the appropriate permissions for the + * requested change. This handle should not be a pipe handle. + * @param FileInformationClass + * A FILE_INFO_BY_HANDLE_CLASS enumeration value that specifies the type of information to be changed. + * Valid values are FILE_BASIC_INFO, FILE_RENAME_INFO, FILE_DISPOSITION_INFO, FILE_ALLOCATION_INFO, + * FILE_END_OF_FILE_INFO, and FILE_IO_PRIORITY_HINT_INFO + * @param lpFileInformation + * A pointer to the buffer that contains the information to change for the specified file + * information class. The structure that this parameter points to corresponds to the class + * that is specified by FileInformationClass. + * @param dwBufferSize + * The size of the lpFileInformation buffer, in bytes. + * @return Returns nonzero if successful or zero otherwise. To get extended error information, call GetLastError. + */ + boolean SetFileInformationByHandle(HANDLE hFile, int FileInformationClass, Pointer lpFileInformation, DWORD dwBufferSize); + /** * Retrieves the date and time that a file or directory was created, last * accessed, and last modified. @@ -2494,6 +2536,18 @@ boolean SystemTimeToTzSpecificLocalTime(TIME_ZONE_INFORMATION lpTimeZone, */ boolean SystemTimeToFileTime(SYSTEMTIME lpSystemTime, FILETIME lpFileTime); + /** + * Converts a file time to system time format. System time is based on Coordinated Universal Time (UTC). + * @param lpFileTime + * [in] A pointer to a FILETIME structure containing the file time to be converted to system (UTC) date and time format. + This value must be less than 0x8000000000000000. Otherwise, the function fails. + * @param lpSystemTime + * A pointer to a SYSTEMTIME structure to receive the converted file time. + * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. + * To get extended error information, call GetLastError. + */ + boolean FileTimeToSystemTime(FILETIME lpFileTime, SYSTEMTIME lpSystemTime); + /** * Creates a thread that runs in the virtual address space of another process. * @@ -2627,6 +2681,100 @@ boolean SystemTimeToTzSpecificLocalTime(TIME_ZONE_INFORMATION lpTimeZone, */ int QueryDosDevice(String lpDeviceName, char[] lpTargetPath, int ucchMax); + /** + * Searches a directory for a file or subdirectory with a name that matches a specific name (or partial name if wildcards are used). + * To specify additional attributes to use in a search, use the FindFirstFileEx function. + * @param lpFileName + * The directory or path, and the file name. The file name can include wildcard characters, + * for example, an asterisk (*) or a question mark (?). + * This parameter should not be NULL, an invalid string (for example, an empty string or a string that is + * missing the terminating null character), or end in a trailing backslash (\). + * If the string ends with a wildcard, period, or directory name, the user must have access to the root + * and all subdirectories on the path. + * In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this + * limit to approximately 32,000 wide characters, call the Unicode version of the function (FindFirstFileExW), + * and prepend "\\?\" to the path. For more information, see Naming a File. + * Tip Starting in Windows 10, version 1607, for the unicode version of this function (FindFirstFileExW), + * you can opt-in to remove the MAX_PATH character limitation without prepending "\\?\". + * See the "Maximum Path Limitation" section of Naming Files, Paths, and Namespaces for details. + * @param lpFindFileData + * A pointer to the buffer that receives the file data. The pointer type is determined by the level of + * information that is specified in the fInfoLevelId parameter. + * @return If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose, + * and the lpFindFileData parameter contains information about the first file or directory found. + * If the function fails or fails to locate files from the search string in the lpFileName parameter, the return + * value is INVALID_HANDLE_VALUE and the contents of lpFindFileData are indeterminate. + * To get extended error information, call the GetLastError function. + * If the function fails because no matching files can be found, the GetLastError function returns ERROR_FILE_NOT_FOUND. + */ + HANDLE FindFirstFile(String lpFileName, Pointer lpFindFileData); + + /** + * Searches a directory for a file or subdirectory with a name and attributes that match those specified. For the most basic + * version of this function, see FindFirstFile. + * @param lpFileName + * The directory or path, and the file name. The file name can include wildcard characters, + * for example, an asterisk (*) or a question mark (?). + * This parameter should not be NULL, an invalid string (for example, an empty string or a string that is + * missing the terminating null character), or end in a trailing backslash (\). + * If the string ends with a wildcard, period, or directory name, the user must have access to the root + * and all subdirectories on the path. + * In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this + * limit to approximately 32,000 wide characters, call the Unicode version of the function (FindFirstFileExW), + * and prepend "\\?\" to the path. For more information, see Naming a File. + * Tip Starting in Windows 10, version 1607, for the unicode version of this function (FindFirstFileExW), + * you can opt-in to remove the MAX_PATH character limitation without prepending "\\?\". + * See the "Maximum Path Limitation" section of Naming Files, Paths, and Namespaces for details. + * @param fInfoLevelId + * The information level of the returned data. This parameter is one of the FINDEX_INFO_LEVELS enumeration values. + * @param lpFindFileData + * A pointer to the buffer that receives the file data. The pointer type is determined by the level of + * information that is specified in the fInfoLevelId parameter. + * @param fSearchOp + * The type of filtering to perform that is different from wildcard matching. This parameter is one of + * the FINDEX_SEARCH_OPS enumeration values. + * @param lpSearchFilter + * A pointer to the search criteria if the specified fSearchOp needs structured search information. + * At this time, none of the supported fSearchOp values require extended search information. Therefore, + * this pointer must be NULL. + * @param dwAdditionalFlags + * Specifies additional flags that control the search. + * FIND_FIRST_EX_CASE_SENSITIVE (0x01) - Searches are case-sensitive. + * FIND_FIRST_EX_LARGE_FETCH (0x02) - Uses a larger buffer for directory queries, which can increase performance + * of the find operation. Windows Server 2008, Windows Vista, Windows Server 2003 and Windows XP: This value + * is not supported until Windows Server 2008 R2 and Windows 7. + * @return If the function succeeds, the return value is a search handle used in a subsequent call to FindNextFile or FindClose, + * and the lpFindFileData parameter contains information about the first file or directory found. + * If the function fails or fails to locate files from the search string in the lpFileName parameter, the return + * value is INVALID_HANDLE_VALUE and the contents of lpFindFileData are indeterminate. + * To get extended error information, call the GetLastError function. + * If the function fails because no matching files can be found, the GetLastError function returns ERROR_FILE_NOT_FOUND. + */ + HANDLE FindFirstFileEx(String lpFileName, int fInfoLevelId, Pointer lpFindFileData, int fSearchOp, Pointer lpSearchFilter, DWORD dwAdditionalFlags); + + /** + * Continues a file search from a previous call to the FindFirstFile, FindFirstFileEx, or FindFirstFileTransacted functions. + * @param hFindFile + * The search handle returned by a previous call to the FindFirstFile or FindFirstFileEx function. + * @param lpFindFileData + * A pointer to the WIN32_FIND_DATA structure that receives information about the found file or subdirectory. + * @return If the function succeeds, the return value is nonzero and the lpFindFileData parameter contains + * information about the next file or directory found. If the function fails, the return value is zero and the + * contents of lpFindFileData are indeterminate. To get extended error information, call the GetLastError function. + * If the function fails because no more matching files can be found, the GetLastError function returns ERROR_NO_MORE_FILES. + */ + boolean FindNextFile(HANDLE hFindFile, Pointer lpFindFileData); + + /** + * Closes a file search handle opened by the FindFirstFile, FindFirstFileEx, FindFirstFileNameW, FindFirstFileNameTransactedW, + * FindFirstFileTransacted, FindFirstStreamTransactedW, or FindFirstStreamW functions. + * @param hFindFile + * The file search handle. + * @return If the function succeeds, the return value is nonzero. If the function fails, the return value is zero. + * To get extended error information, call GetLastError. + */ + boolean FindClose(HANDLE hFindFile); + /** * Retrieves the name of a mounted folder on the specified volume - used * to begin scanning the mounted folders on a volume diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Ntifs.java b/contrib/platform/src/com/sun/jna/platform/win32/Ntifs.java new file mode 100644 index 0000000000..bb868b28c7 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/Ntifs.java @@ -0,0 +1,402 @@ +/* Copyright (c) 2016 Adam Marcionek, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32; + +import java.util.Arrays; +import java.util.List; + +import com.sun.jna.Native; +import com.sun.jna.Pointer; +import com.sun.jna.Structure; +import com.sun.jna.Union; +import com.sun.jna.win32.W32APITypeMapper; + +/** + * Ported from Ntifs.h + * Microsoft Windows WDK 10 + * @author amarcionek[at]gmail.com + */ +public interface Ntifs extends WinDef, BaseTSD { + + // Defined in winnt.h + public int MAXIMUM_REPARSE_DATA_BUFFER_SIZE = 16 * 1024; + + // 1 long and 2 shorts aligned on longlong + public int REPARSE_BUFFER_HEADER_SIZE = 8; + + public int SYMLINK_FLAG_RELATIVE = 1; + + public static class SymbolicLinkReparseBuffer extends Structure { + + public static class ByReference extends SymbolicLinkReparseBuffer implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * Offset, in bytes, of the substitute name string in the PathBuffer array. + * Note that this offset must be divided by sizeof(WCHAR) to get the array index. + */ + public short SubstituteNameOffset = 0; + + /** + * Length, in bytes, of the substitute name string. If this string is NULL-terminated, + * SubstituteNameLength does not include space for the UNICODE_NULL character. + */ + public short SubstituteNameLength = 0; + + /** + * Offset, in bytes, of the print name string in the PathBuffer array. + * Note that this offset must be divided by sizeof(WCHAR) to get the array index. + */ + public short PrintNameOffset = 0; + + /** + * Length, in bytes, of the print name string. If this string is NULL-terminated, + * PrintNameLength does not include space for the UNICODE_NULL character. + */ + public short PrintNameLength = 0; + + /** + * Used to indicate if the given symbolic link is an absolute or relative symbolic link. + * If Flags contains SYMLINK_FLAG_RELATIVE, the symbolic link contained in the PathBuffer + * array (at offset SubstitueNameOffset) is processed as a relative symbolic link; otherwise, + * it is processed as an absolute symbolic link. + */ + public int Flags = 0; + + /** + * First character of the path string. This is followed in memory by the remainder of the string. + * The path string contains the substitute name string and print name string. The substitute name + * and print name strings can appear in any order in the PathBuffer. (To locate the substitute + * name and print name strings in the PathBuffer, use the SubstituteNameOffset, SubstituteNameLength, + * PrintNameOffset, and PrintNameLength members.) + * NOTE: MAXIMUM_REPARSE_DATA_BUFFER_SIZE is chosen here based on documentation. Because chars are two + * bytes, the actual array size needs to be divided by 2 + */ + public char[] PathBuffer = new char[MAXIMUM_REPARSE_DATA_BUFFER_SIZE / 2]; + + public static int sizeOf() { + return Native.getNativeSize(MountPointReparseBuffer.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "SubstituteNameOffset", "SubstituteNameLength", "PrintNameOffset", "PrintNameLength", "Flags", "PathBuffer" }); + } + + public SymbolicLinkReparseBuffer() { + super(W32APITypeMapper.UNICODE); + } + + public SymbolicLinkReparseBuffer(Pointer memory) { + super(memory, Structure.ALIGN_DEFAULT, W32APITypeMapper.UNICODE); + read(); + } + + public SymbolicLinkReparseBuffer(String substituteName, String printName, int Flags) { + super(); + String bothNames = substituteName + printName; + PathBuffer = bothNames.toCharArray(); + this.SubstituteNameOffset = 0; + this.SubstituteNameLength = (short) (substituteName.length() * 2); + this.PrintNameOffset = (short) (substituteName.length() * 2); + this.PrintNameLength = (short) (printName.length() * 2); + this.Flags = Flags; + write(); + } + + public SymbolicLinkReparseBuffer(short SubstituteNameOffset, short SubstituteNameLength, short PrintNameOffset, short PrintNameLength, int Flags, String PathBuffer) { + super(); + this.SubstituteNameOffset = SubstituteNameOffset; + this.SubstituteNameLength = SubstituteNameLength; + this.PrintNameOffset = PrintNameOffset; + this.PrintNameLength = PrintNameLength; + this.Flags = Flags; + this.PathBuffer = PathBuffer.toCharArray(); + write(); + } + + /** + * @return the print name in a String + */ + public String getPrintName() { + return String.copyValueOf(PathBuffer, PrintNameOffset / 2, PrintNameLength / 2); + } + + /** + * @return the substitute name in a String + */ + public String getSubstituteName() { + return String.copyValueOf(PathBuffer, SubstituteNameOffset / 2, SubstituteNameLength / 2); + } + } + + public static class MountPointReparseBuffer extends Structure { + + public static class ByReference extends MountPointReparseBuffer implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * Offset, in bytes, of the substitute name string in the PathBuffer array. + * Note that this offset must be divided by sizeof(WCHAR) to get the array index. + */ + public short SubstituteNameOffset = 0; + + /** + * Length, in bytes, of the substitute name string. If this string is NULL-terminated, + * SubstituteNameLength does not include space for the UNICODE_NULL character. + */ + public short SubstituteNameLength = 0; + + /** + * Offset, in bytes, of the print name string in the PathBuffer array. + * Note that this offset must be divided by sizeof(WCHAR) to get the array index. + */ + public short PrintNameOffset = 0; + + /** + * Length, in bytes, of the print name string. If this string is NULL-terminated, + * PrintNameLength does not include space for the UNICODE_NULL character. + */ + public short PrintNameLength = 0; + + /** + * First character of the path string. This is followed in memory by the remainder of the string. + * The path string contains the substitute name string and print name string. The substitute name + * and print name strings can appear in any order in the PathBuffer. (To locate the substitute + * name and print name strings in the PathBuffer, use the SubstituteNameOffset, SubstituteNameLength, + * PrintNameOffset, and PrintNameLength members.) + * NOTE: MAXIMUM_REPARSE_DATA_BUFFER_SIZE is chosen here based on documentation. Because chars are two + * bytes, the actual array size needs to be divided by 2 + */ + public char[] PathBuffer = new char[MAXIMUM_REPARSE_DATA_BUFFER_SIZE / 2]; + + public static int sizeOf() { + return Native.getNativeSize(MountPointReparseBuffer.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "SubstituteNameOffset", "SubstituteNameLength", "PrintNameOffset", "PrintNameLength", "PathBuffer" }); + } + + public MountPointReparseBuffer() { + super(W32APITypeMapper.UNICODE); + } + + public MountPointReparseBuffer(Pointer memory) { + super(memory, Structure.ALIGN_DEFAULT, W32APITypeMapper.UNICODE); + read(); + } + + public MountPointReparseBuffer(String substituteName, String printName) { + super(); + String bothNames = substituteName + printName; + PathBuffer = bothNames.toCharArray(); + this.SubstituteNameOffset = 0; + this.SubstituteNameLength = (short) substituteName.length(); + this.PrintNameOffset = (short) (substituteName.length() * 2); + this.PrintNameLength = (short) (printName.length() * 2); + write(); + } + + public MountPointReparseBuffer(short SubstituteNameOffset, short SubstituteNameLength, short PrintNameOffset, short PrintNameLength, String PathBuffer) { + super(); + this.SubstituteNameOffset = SubstituteNameOffset; + this.SubstituteNameLength = SubstituteNameLength; + this.PrintNameOffset = PrintNameOffset; + this.PrintNameLength = PrintNameLength; + this.PathBuffer = PathBuffer.toCharArray(); + write(); + } + } + + public static class GenericReparseBuffer extends Structure { + + public static class ByReference extends GenericReparseBuffer implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * Microsoft-defined data for the reparse point. + * NOTE: MAXIMUM_REPARSE_DATA_BUFFER_SIZE is chosen based on documentation + */ + public byte[] DataBuffer = new byte[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; + + public static int sizeOf() { + return Native.getNativeSize(GenericReparseBuffer.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "DataBuffer" }); + } + + public GenericReparseBuffer() { + super(); + } + + public GenericReparseBuffer(Pointer memory) { + super(memory); + read(); + } + + public GenericReparseBuffer(String DataBuffer) { + super(); + this.DataBuffer = DataBuffer.getBytes(); + write(); + } + } + + /** + * The REPARSE_DATA_BUFFER structure contains reparse point data for a Microsoft reparse point. + * (Third-party reparse point owners must use the REPARSE_GUID_DATA_BUFFER structure instead.) + */ + public static class REPARSE_DATA_BUFFER extends Structure { + + public static class ByReference extends REPARSE_DATA_BUFFER implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * Reparse point tag. Must be a Microsoft reparse point tag. + */ + public int ReparseTag = 0; + + /** + * Size, in bytes, of the reparse data in the DataBuffer member. + */ + public short ReparseDataLength = 0; + + /** + * Length, in bytes, of the unparsed portion of the file name pointed to by the FileName member of the associated file object. + * For more information about the FileName member, see FILE_OBJECT. This member is only valid for create operations when the + * I/O fails with STATUS_REPARSE. For all other purposes, such as setting or querying a reparse point for the reparse data, + * this member is treated as reserved. + */ + public short Reserved = 0; + + public static class REPARSE_UNION extends Union { + public static class ByReference extends REPARSE_UNION implements Structure.ByReference { + + } + + public REPARSE_UNION() { + super(); + } + + public REPARSE_UNION(Pointer memory) { + super(memory); + } + + public SymbolicLinkReparseBuffer symLinkReparseBuffer; + public MountPointReparseBuffer mountPointReparseBuffer; + public GenericReparseBuffer genericReparseBuffer; + } + + public REPARSE_UNION u; + + public static int sizeOf() { + return Native.getNativeSize(REPARSE_DATA_BUFFER.class, null); + } + + /** + * @return size of the structure considering the ReparseDataLength size + */ + public int getSize() { + return REPARSE_BUFFER_HEADER_SIZE + ReparseDataLength; + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "ReparseTag", "ReparseDataLength", "Reserved", "u" }); + } + + public REPARSE_DATA_BUFFER() { + super(); + } + + public REPARSE_DATA_BUFFER(int ReparseTag, short Reserved) { + super(); + this.ReparseTag = ReparseTag; + this.Reserved = Reserved; + this.ReparseDataLength = 0; + write(); + } + + public REPARSE_DATA_BUFFER(int ReparseTag, short Reserved, SymbolicLinkReparseBuffer symLinkReparseBuffer) { + super(); + this.ReparseTag = ReparseTag; + this.Reserved = Reserved; + this.ReparseDataLength = (short) symLinkReparseBuffer.size(); + this.u.setType(SymbolicLinkReparseBuffer.class); + this.u.symLinkReparseBuffer = symLinkReparseBuffer; + write(); + } + + public REPARSE_DATA_BUFFER(Pointer memory) { + super(memory); + read(); + } + + @Override + public void read() { + super.read(); + // Set structure value based on ReparseTag and then re-read the union. + switch(ReparseTag) { + default: + u.setType(GenericReparseBuffer.class); + break; + case WinNT.IO_REPARSE_TAG_MOUNT_POINT: + u.setType(MountPointReparseBuffer.class); + break; + case WinNT.IO_REPARSE_TAG_SYMLINK: + u.setType(SymbolicLinkReparseBuffer.class); + break; + } + u.read(); + } + } +} diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java b/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java index fb483014a7..79f5bd031e 100755 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinBase.java @@ -30,11 +30,13 @@ import java.util.List; import com.sun.jna.Callback; +import com.sun.jna.Native; import com.sun.jna.Platform; import com.sun.jna.Pointer; import com.sun.jna.Structure; import com.sun.jna.Union; import com.sun.jna.platform.win32.WinNT.HANDLE; +import com.sun.jna.platform.win32.WinNT.LARGE_INTEGER; import com.sun.jna.ptr.ByteByReference; import com.sun.jna.win32.StdCallLibrary.StdCallCallback; import com.sun.jna.win32.W32APITypeMapper; @@ -194,6 +196,640 @@ public interface WinBase extends WinDef, BaseTSD { */ int STILL_ACTIVE = WinNT.STATUS_PENDING; + // Codes for FILE_INFO_BY_HANDLE_CLASS taken from Winbase.h + int FileBasicInfo = 0; + int FileStandardInfo = 1; + int FileNameInfo = 2; + int FileRenameInfo = 3; + int FileDispositionInfo = 4; + int FileAllocationInfo = 5; + int FileEndOfFileInfo = 6; + int FileStreamInfo = 7; + int FileCompressionInfo = 8; + int FileAttributeTagInfo = 9; + int FileIdBothDirectoryInfo = 10; // 0xA + int FileIdBothDirectoryRestartInfo = 11; // 0xB + int FileIoPriorityHintInfo = 12; // 0xC + int FileRemoteProtocolInfo = 13; // 0xD + int FileFullDirectoryInfo = 14; // 0xE + int FileFullDirectoryRestartInfo = 15; // 0xF + int FileStorageInfo = 16; // 0x10 + int FileAlignmentInfo = 17; // 0x11 + int FileIdInfo = 18; // 0x12 + int FileIdExtdDirectoryInfo = 19; // 0x13 + int FileIdExtdDirectoryRestartInfo = 20; // 0x14 + + /** + * Contains the basic information for a file. Used for file handles. + */ + public static class FILE_BASIC_INFO extends Structure { + + public static class ByReference extends FILE_BASIC_INFO implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * The time the file was created in FILETIME format, which is a 64-bit value + * representing the number of 100-nanosecond intervals since January 1, 1601 (UTC). + */ + public LARGE_INTEGER CreationTime; + + /** + * The time the file was last accessed in FILETIME format. + */ + public LARGE_INTEGER LastAccessTime; + + /** + * The time the file was last written to in FILETIME format. + */ + public LARGE_INTEGER LastWriteTime; + + /** + * The time the file was changed in FILETIME format. + */ + public LARGE_INTEGER ChangeTime; + + /** + * The file attributes. For a list of attributes, see File Attribute Constants. + * If this is set to 0 in a FILE_BASIC_INFO structure passed to SetFileInformationByHandle + * then none of the attributes are changed. + */ + public int FileAttributes; + + public static int sizeOf() + { + return Native.getNativeSize(FILE_BASIC_INFO.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "CreationTime", "LastAccessTime", "LastWriteTime", "ChangeTime", "FileAttributes" }); + } + + public FILE_BASIC_INFO() { + super(); + } + + public FILE_BASIC_INFO(Pointer memory) { + super(memory); + read(); + // This is admittedly odd, but the read() doesn't properly initialize the LARGE_INTEGERs via contructors, so do so here. + this.CreationTime = new LARGE_INTEGER(this.CreationTime.getValue()); + this.LastAccessTime = new LARGE_INTEGER(this.LastAccessTime.getValue()); + this.LastWriteTime = new LARGE_INTEGER(this.LastWriteTime.getValue()); + this.ChangeTime = new LARGE_INTEGER(this.ChangeTime.getValue()); + } + + public FILE_BASIC_INFO(FILETIME CreationTime, + FILETIME LastAccessTime, + FILETIME LastWriteTime, + FILETIME ChangeTime, + int FileAttributes) { + this.CreationTime = new LARGE_INTEGER(CreationTime.toTime()); + this.LastAccessTime = new LARGE_INTEGER(LastAccessTime.toTime()); + this.LastWriteTime = new LARGE_INTEGER(LastWriteTime.toTime()); + this.ChangeTime = new LARGE_INTEGER(ChangeTime.toTime()); + this.FileAttributes = FileAttributes; + write(); + } + + public FILE_BASIC_INFO(LARGE_INTEGER CreationTime, + LARGE_INTEGER LastAccessTime, + LARGE_INTEGER LastWriteTime, + LARGE_INTEGER ChangeTime, + int FileAttributes) { + this.CreationTime = CreationTime; + this.LastAccessTime = LastAccessTime; + this.LastWriteTime = LastWriteTime; + this.ChangeTime = ChangeTime; + this.FileAttributes = FileAttributes; + write(); + } + } + + /** + * Receives extended information for the file. Used for file handles. Use only when calling GetFileInformationByHandleEx. + */ + public static class FILE_STANDARD_INFO extends Structure { + + public static class ByReference extends FILE_STANDARD_INFO implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * The amount of space that is allocated for the file. + */ + public LARGE_INTEGER AllocationSize; + + /** + * The end of the file. + */ + public LARGE_INTEGER EndOfFile; + + /** + * The number of links to the file. + */ + public int NumberOfLinks; + + /** + * TRUE if the file in the delete queue; otherwise, false. + */ + public boolean DeletePending; + + /** + * TRUE if the file is a directory; otherwise, false. + */ + public boolean Directory; + + public static int sizeOf() + { + return Native.getNativeSize(FILE_STANDARD_INFO.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "AllocationSize", "EndOfFile", "NumberOfLinks", "DeletePending", "Directory" }); + } + + public FILE_STANDARD_INFO() { + super(); + } + + public FILE_STANDARD_INFO(Pointer memory) { + super(memory); + read(); + } + + public FILE_STANDARD_INFO(LARGE_INTEGER AllocationSize, + LARGE_INTEGER EndOfFile, + int NumberOfLinks, + boolean DeletePending, + boolean Directory) { + this.AllocationSize = AllocationSize; + this.EndOfFile = EndOfFile; + this.NumberOfLinks = NumberOfLinks; + this.DeletePending = DeletePending; + this.Directory = Directory; + write(); + } + } + + /** + * Indicates whether a file should be deleted. Used for any handles. Use only when calling SetFileInformationByHandle. + */ + public static class FILE_DISPOSITION_INFO extends Structure { + + public static class ByReference extends FILE_DISPOSITION_INFO implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * Indicates whether the file should be deleted. Set to TRUE to delete the file. This member + * has no effect if the handle was opened with FILE_FLAG_DELETE_ON_CLOSE. + */ + public boolean DeleteFile; + + public static int sizeOf() + { + return Native.getNativeSize(FILE_DISPOSITION_INFO.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "DeleteFile" }); + } + + public FILE_DISPOSITION_INFO () { + super(); + } + + public FILE_DISPOSITION_INFO (Pointer memory) { + super(memory); + read(); + } + + public FILE_DISPOSITION_INFO (boolean DeleteFile) { + this.DeleteFile = DeleteFile; + write(); + } + } + + /** + * Receives extended information for the file. Used for file handles. Use only when calling GetFileInformationByHandleEx. + */ + public static class FILE_COMPRESSION_INFO extends Structure { + + public static class ByReference extends FILE_COMPRESSION_INFO implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * The file size of the compressed file. + */ + public LARGE_INTEGER CompressedFileSize; + + /** + * The compression format that is used to compress the file. + */ + public short CompressionFormat; + + /** + * The factor that the compression uses. + */ + public byte CompressionUnitShift; + + /** + * The number of chunks that are shifted by compression. + */ + public byte ChunkShift; + + /** + * The number of clusters that are shifted by compression. + */ + public byte ClusterShift; + + /** + * Reserved + */ + public byte[] Reserved = new byte[3]; + + public static int sizeOf() + { + return Native.getNativeSize(FILE_COMPRESSION_INFO.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "CompressedFileSize", "CompressionFormat", "CompressionUnitShift", "ChunkShift", "ClusterShift", "Reserved" }); + } + + public FILE_COMPRESSION_INFO() { + super(W32APITypeMapper.DEFAULT); + } + + public FILE_COMPRESSION_INFO(Pointer memory) { + super(memory, Structure.ALIGN_DEFAULT, W32APITypeMapper.DEFAULT); + read(); + } + + public FILE_COMPRESSION_INFO(LARGE_INTEGER CompressedFileSize, + short CompressionFormat, + byte CompressionUnitShift, + byte ChunkShift, + byte ClusterShift) { + this.CompressedFileSize = CompressedFileSize; + this.CompressionFormat = CompressionFormat; + this.CompressionUnitShift = CompressionUnitShift; + this.ChunkShift = ChunkShift; + this.ClusterShift = ClusterShift; + this.Reserved = new byte[3]; + write(); + } + } + + /** + * Receives the requested file attribute information. Used for any handles. Use only when calling GetFileInformationByHandleEx. + */ + public static class FILE_ATTRIBUTE_TAG_INFO extends Structure { + + public static class ByReference extends FILE_ATTRIBUTE_TAG_INFO implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * The file attribute information. + */ + public int FileAttributes; + + /** + * The reparse tag. + */ + public int ReparseTag; + + public static int sizeOf() + { + return Native.getNativeSize(FILE_ATTRIBUTE_TAG_INFO.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "FileAttributes", "ReparseTag" }); + } + + public FILE_ATTRIBUTE_TAG_INFO() { + super(); + } + + public FILE_ATTRIBUTE_TAG_INFO(Pointer memory) { + super(memory); + read(); + } + + public FILE_ATTRIBUTE_TAG_INFO(int FileAttributes, + int ReparseTag) { + this.FileAttributes = FileAttributes; + this.ReparseTag = ReparseTag; + write(); + } + } + + /** + * Contains identification information for a file. This structure is returned from the + * GetFileInformationByHandleEx function when FileIdInfo is passed in the + * FileInformationClass parameter. + */ + public static class FILE_ID_INFO extends Structure { + + public static class ByReference extends FILE_ID_INFO implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + public static class FILE_ID_128 extends Structure { + public BYTE[] Identifier = new BYTE[16]; + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "Identifier" }); + } + + public FILE_ID_128() { + super(); + } + + public FILE_ID_128(Pointer memory) { + super(memory); + read(); + } + + public FILE_ID_128(BYTE[] Identifier) { + this.Identifier = Identifier; + write(); + } + } + + /** + * The serial number of the volume that contains a file. + */ + public long VolumeSerialNumber; + + /** + * The end of the file. + */ + public FILE_ID_128 FileId; + + public static int sizeOf() + { + return Native.getNativeSize(FILE_ID_INFO.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "VolumeSerialNumber", "FileId" }); + } + + public FILE_ID_INFO() { + super(); + } + + public FILE_ID_INFO(Pointer memory) { + super(memory); + read(); + } + + public FILE_ID_INFO(long VolumeSerialNumber, + FILE_ID_128 FileId) { + this.VolumeSerialNumber = VolumeSerialNumber; + this.FileId = FileId; + write(); + } + } + + // FINDEX_INFO_LEVELS values defines values that are used with the FindFirstFileEx function to specify the information level of the returned data. + + /** + * The FindFirstFileEx function retrieves a standard set of attribute information. The data is returned + * in a WIN32_FIND_DATA structure. + */ + int FindExInfoStandard = 0; + + /** + * The FindFirstFileEx function does not query the short file name, improving overall enumeration speed. The data is + * returned in a WIN32_FIND_DATA structure, and the cAlternateFileName member is always a NULL string. + */ + int FindExInfoBasic = 1; + /** + * This value is used for validation. Supported values are less than this value. + */ + int FindExInfoMaxInfoLevel = 2; + + // FINDEX_SEARCH_OPS values defines values that are used with the FindFirstFileEx function to specify the type of filtering to perform. + /** + * The search for a file that matches a specified file name. The lpSearchFilter parameter of FindFirstFileEx + * must be NULL when this search operation is used. + */ + int FindExSearchNameMatch = 0; + + /** + * This is an advisory flag. If the file system supports directory filtering, the function searches for a file that + * matches the specified name and is also a directory. If the file system does not support directory filtering, + * this flag is silently ignored. + * The lpSearchFilter parameter of the FindFirstFileEx function must be NULL when this search value is used. + * If directory filtering is desired, this flag can be used on all file systems, but because it is an advisory + * flag and only affects file systems that support it, the application must examine the file attribute data stored + * in the lpFindFileData parameter of the FindFirstFileEx function to determine whether the function has returned + * a handle to a directory. + */ + int FindExSearchLimitToDirectories = 1; + + /** + * This filtering type is not available. For more information, see Device Interface Classes. + */ + int FindExSearchLimitToDevices = 2; + + /** + * Contains information about the file that is found by the FindFirstFile, FindFirstFileEx, or FindNextFile function. + */ + public static class WIN32_FIND_DATA extends Structure { + + public static class ByReference extends WIN32_FIND_DATA implements Structure.ByReference { + public ByReference() { + } + + public ByReference(Pointer memory) { + super(memory); + } + } + + /** + * The file attributes of a file. For possible values and their descriptions, + * see File Attribute Constants. The FILE_ATTRIBUTE_SPARSE_FILE attribute on + * the file is set if any of the streams of the file have ever been sparse. + */ + public int dwFileAttributes; + + /** + * A FILETIME structure that specifies when a file or directory was created. If + * the underlying file system does not support creation time, this member is zero. + */ + public FILETIME ftCreationTime; + + /** + * A FILETIME structure. For a file, the structure specifies when the file was last + * read from, written to, or for executable files, run. For a directory, the structure + * specifies when the directory is created. If the underlying file system does not + * support last access time, this member is zero. On the FAT file system, the + * specified date for both files and directories is correct, but the time of day is + * always set to midnight. + */ + public FILETIME ftLastAccessTime; + + /** + * A FILETIME structure. For a file, the structure specifies when the file was last + * written to, truncated, or overwritten, for example, when WriteFile or SetEndOfFile + * are used. The date and time are not updated when file attributes or security descriptors + * are changed. For a directory, the structure specifies when the directory is created. + * If the underlying file system does not support last write time, this member is zero. + */ + public FILETIME ftLastWriteTime; + + /** + * The high-order DWORD value of the file size, in bytes. This value is zero unless the + * file size is greater than MAXDWORD. + * The size of the file is equal to (nFileSizeHigh * (MAXDWORD+1)) + nFileSizeLow. + */ + public int nFileSizeHigh; + + /** + * The low-order DWORD value of the file size, in bytes. + */ + public int nFileSizeLow; + + /** + * If the dwFileAttributes member includes the FILE_ATTRIBUTE_REPARSE_POINT attribute, this member + * specifies the reparse point tag. Otherwise, this value is undefined and should not be used. + * For more information see Reparse Point Tags. + * + * IO_REPARSE_TAG_CSV (0x80000009) + * IO_REPARSE_TAG_DEDUP (0x80000013) + * IO_REPARSE_TAG_DFS (0x8000000A) + * IO_REPARSE_TAG_DFSR (0x80000012) + * IO_REPARSE_TAG_HSM (0xC0000004) + * IO_REPARSE_TAG_HSM2 (0x80000006) + * IO_REPARSE_TAG_MOUNT_POINT (0xA0000003) + * IO_REPARSE_TAG_NFS (0x80000014) + * IO_REPARSE_TAG_SIS (0x80000007) + * IO_REPARSE_TAG_SYMLINK (0xA000000C) + * IO_REPARSE_TAG_WIM (0x80000008) + */ + public int dwReserved0; + + /** + * Reserved for future use. + */ + public int dwReserved1; + + /** + * The name of the file. NOTE: When written from Native memory, this will be a null terminated string. + * Any characters after the null terminator are random memory. Use function getFileName to + * get a String with the name. + */ + public char[] cFileName = new char[MAX_PATH]; + + /** + * An alternative name for the file. This name is in the classic 8.3 file name format. + * NOTE: When written from Native memory, this will be a null terminated string. + * Any characters after the null terminator are random memory. Use function getAlternateFileName to + * get a String with the alternate name. + */ + public char[] cAlternateFileName = new char[14]; + + public static int sizeOf() { + return Native.getNativeSize(WIN32_FIND_DATA.class, null); + } + + @Override + protected List getFieldOrder() { + return Arrays.asList(new String[] { "dwFileAttributes", "ftCreationTime", "ftLastAccessTime", "ftLastWriteTime", "nFileSizeHigh", "nFileSizeLow", "dwReserved0", "dwReserved1", "cFileName", "cAlternateFileName" }); + } + + public WIN32_FIND_DATA() { + super(W32APITypeMapper.DEFAULT); + } + + public WIN32_FIND_DATA(Pointer memory) { + super(memory, Structure.ALIGN_DEFAULT, W32APITypeMapper.DEFAULT); + read(); + } + + public WIN32_FIND_DATA(int dwFileAttributes, + FILETIME ftCreationTime, + FILETIME ftLastAccessTime, + FILETIME ftLastWriteTime, + int nFileSizeHigh, + int nFileSizeLow, + int dwReserved0, + int dwReserved1, + char[] cFileName, + char[] cAlternateFileName) { + this.dwFileAttributes = dwFileAttributes; + this.ftCreationTime = ftCreationTime; + this.ftLastAccessTime = ftLastAccessTime; + this.ftLastWriteTime = ftLastWriteTime; + this.nFileSizeHigh = nFileSizeHigh; + this.nFileSizeLow = nFileSizeLow; + this.dwReserved0 = dwReserved0; + this.cFileName = cFileName; + this.cAlternateFileName = cAlternateFileName; + write(); + } + + /** + * @return String containing the file name + */ + public String getFileName() { + String actualFileName = new String(cFileName); + return actualFileName.substring(0, actualFileName.indexOf('\0')); + } + + /** + * @return String containing the alternate file name + */ + public String getAlternateFileName() { + String actualAlternateFileName = new String(cAlternateFileName); + return actualAlternateFileName.substring(0, actualAlternateFileName.indexOf('\0')); + } + } + /** * The FILETIME structure is a 64-bit value representing the number of * 100-nanosecond intervals since January 1, 1601 (UTC). @@ -224,6 +860,15 @@ public FILETIME(Date date) { dwLowDateTime = (int)(rawValue & 0xffffffffL); } + /** + * Construct FILETIME from LARGE_INTEGER + * @param ft + */ + public FILETIME(LARGE_INTEGER ft) { + dwHighDateTime = ft.getHigh().intValue(); + dwLowDateTime = ft.getLow().intValue(); + } + public FILETIME() { } diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java index 8290369cac..5dcf09bf07 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinNT.java @@ -839,6 +839,16 @@ public abstract class SID_NAME_USE { int FILE_SUPPORTS_OPEN_BY_FILE_ID = 0x01000000; int FILE_SUPPORTS_USN_JOURNAL = 0x02000000; + // Reparse point tags + int IO_REPARSE_TAG_MOUNT_POINT = 0xA0000003; + int IO_REPARSE_TAG_HSM = 0xC0000004; + int IO_REPARSE_TAG_HSM2 = 0x80000006; + int IO_REPARSE_TAG_SIS = 0x80000007; + int IO_REPARSE_TAG_WIM = 0x80000008; + int IO_REPARSE_TAG_CSV = 0x80000009; + int IO_REPARSE_TAG_DFS = 0x8000000A; + int IO_REPARSE_TAG_SYMLINK = 0xA000000C; + int IO_REPARSE_TAG_DFSR = 0x80000012; // The controllable aspects of the DefineDosDevice function. // see https://msdn.microsoft.com/en-us/library/windows/desktop/aa363904(v=vs.85).aspx @@ -847,6 +857,15 @@ public abstract class SID_NAME_USE { int DDD_EXACT_MATCH_ON_REMOVE = 0x00000004; int DDD_NO_BROADCAST_SYSTEM = 0x00000008; + int COMPRESSION_FORMAT_NONE = 0x0000; + int COMPRESSION_FORMAT_DEFAULT = 0x0001; + int COMPRESSION_FORMAT_LZNT1 = 0x0002; + int COMPRESSION_FORMAT_XPRESS = 0x0003; + int COMPRESSION_FORMAT_XPRESS_HUFF = 0x0004; + int COMPRESSION_ENGINE_STANDARD = 0x0000; + int COMPRESSION_ENGINE_MAXIMUM = 0x0100; + int COMPRESSION_ENGINE_HIBER = 0x0200; + /** * The FILE_NOTIFY_INFORMATION structure describes the changes found by the * ReadDirectoryChangesW function. diff --git a/contrib/platform/src/com/sun/jna/platform/win32/Winioctl.java b/contrib/platform/src/com/sun/jna/platform/win32/Winioctl.java index 6fae3894b5..95700d26a9 100644 --- a/contrib/platform/src/com/sun/jna/platform/win32/Winioctl.java +++ b/contrib/platform/src/com/sun/jna/platform/win32/Winioctl.java @@ -31,6 +31,103 @@ */ public interface Winioctl { + // Devices + public int FILE_DEVICE_BEEP = 0x00000001; + public int FILE_DEVICE_CD_ROM = 0x00000002; + public int FILE_DEVICE_CD_ROM_FILE_SYSTEM = 0x00000003; + public int FILE_DEVICE_CONTROLLER = 0x00000004; + public int FILE_DEVICE_DATALINK = 0x00000005; + public int FILE_DEVICE_DFS = 0x00000006; + public int FILE_DEVICE_DISK = 0x00000007; + public int FILE_DEVICE_DISK_FILE_SYSTEM = 0x00000008; + public int FILE_DEVICE_FILE_SYSTEM = 0x00000009; + public int FILE_DEVICE_INPORT_PORT = 0x0000000a; + public int FILE_DEVICE_KEYBOARD = 0x0000000b; + public int FILE_DEVICE_MAILSLOT = 0x0000000c; + public int FILE_DEVICE_MIDI_IN = 0x0000000d; + public int FILE_DEVICE_MIDI_OUT = 0x0000000e; + public int FILE_DEVICE_MOUSE = 0x0000000f; + public int FILE_DEVICE_MULTI_UNC_PROVIDER = 0x00000010; + public int FILE_DEVICE_NAMED_PIPE = 0x00000011; + public int FILE_DEVICE_NETWORK = 0x00000012; + public int FILE_DEVICE_NETWORK_BROWSER = 0x00000013; + public int FILE_DEVICE_NETWORK_FILE_SYSTEM = 0x00000014; + public int FILE_DEVICE_NULL = 0x00000015; + public int FILE_DEVICE_PARALLEL_PORT = 0x00000016; + public int FILE_DEVICE_PHYSICAL_NETCARD = 0x00000017; + public int FILE_DEVICE_PRINTER = 0x00000018; + public int FILE_DEVICE_SCANNER = 0x00000019; + public int FILE_DEVICE_SERIAL_MOUSE_PORT = 0x0000001a; + public int FILE_DEVICE_SERIAL_PORT = 0x0000001b; + public int FILE_DEVICE_SCREEN = 0x0000001c; + public int FILE_DEVICE_SOUND = 0x0000001d; + public int FILE_DEVICE_STREAMS = 0x0000001e; + public int FILE_DEVICE_TAPE = 0x0000001f; + public int FILE_DEVICE_TAPE_FILE_SYSTEM = 0x00000020; + public int FILE_DEVICE_TRANSPORT = 0x00000021; + public int FILE_DEVICE_UNKNOWN = 0x00000022; + public int FILE_DEVICE_VIDEO = 0x00000023; + public int FILE_DEVICE_VIRTUAL_DISK = 0x00000024; + public int FILE_DEVICE_WAVE_IN = 0x00000025; + public int FILE_DEVICE_WAVE_OUT = 0x00000026; + public int FILE_DEVICE_8042_PORT = 0x00000027; + public int FILE_DEVICE_NETWORK_REDIRECTOR = 0x00000028; + public int FILE_DEVICE_BATTERY = 0x00000029; + public int FILE_DEVICE_BUS_EXTENDER = 0x0000002a; + public int FILE_DEVICE_MODEM = 0x0000002b; + public int FILE_DEVICE_VDM = 0x0000002c; + public int FILE_DEVICE_MASS_STORAGE = 0x0000002d; + public int FILE_DEVICE_SMB = 0x0000002e; + public int FILE_DEVICE_KS = 0x0000002f; + public int FILE_DEVICE_CHANGER = 0x00000030; + public int FILE_DEVICE_SMARTCARD = 0x00000031; + public int FILE_DEVICE_ACPI = 0x00000032; + public int FILE_DEVICE_DVD = 0x00000033; + public int FILE_DEVICE_FULLSCREEN_VIDEO = 0x00000034; + public int FILE_DEVICE_DFS_FILE_SYSTEM = 0x00000035; + public int FILE_DEVICE_DFS_VOLUME = 0x00000036; + public int FILE_DEVICE_SERENUM = 0x00000037; + public int FILE_DEVICE_TERMSRV = 0x00000038; + public int FILE_DEVICE_KSEC = 0x00000039; + public int FILE_DEVICE_FIPS = 0x0000003A; + public int FILE_DEVICE_INFINIBAND = 0x0000003B; + public int FILE_DEVICE_VMBUS = 0x0000003E; + public int FILE_DEVICE_CRYPT_PROVIDER = 0x0000003F; + public int FILE_DEVICE_WPD = 0x00000040; + public int FILE_DEVICE_BLUETOOTH = 0x00000041; + public int FILE_DEVICE_MT_COMPOSITE = 0x00000042; + public int FILE_DEVICE_MT_TRANSPORT = 0x00000043; + public int FILE_DEVICE_BIOMETRIC = 0x00000044; + public int FILE_DEVICE_PMI = 0x00000045; + public int FILE_DEVICE_EHSTOR = 0x00000046; + public int FILE_DEVICE_DEVAPI = 0x00000047; + public int FILE_DEVICE_GPIO = 0x00000048; + public int FILE_DEVICE_USBEX = 0x00000049; + public int FILE_DEVICE_CONSOLE = 0x00000050; + public int FILE_DEVICE_NFP = 0x00000051; + public int FILE_DEVICE_SYSENV = 0x00000052; + public int FILE_DEVICE_VIRTUAL_BLOCK = 0x00000053; + public int FILE_DEVICE_POINT_OF_SERVICE = 0x00000054; + + // Functions + public int FSCTL_GET_COMPRESSION = 15; + public int FSCTL_SET_COMPRESSION = 16; + public int FSCTL_SET_REPARSE_POINT = 41; + public int FSCTL_GET_REPARSE_POINT = 42; + public int FSCTL_DELETE_REPARSE_POINT = 43; + + // Methods + public int METHOD_BUFFERED = 0; + public int METHOD_IN_DIRECT = 1; + public int METHOD_OUT_DIRECT = 2; + public int METHOD_NEITHER = 3; + + // Access + public int FILE_ANY_ACCESS = 0; + public int FILE_SPECIAL_ACCESS = FILE_ANY_ACCESS; + public int FILE_READ_ACCESS = 0x0001; // file & pipe + public int FILE_WRITE_ACCESS = 0x0002; // file & pipe + /** * Retrieves the device type, device number, and, for a partitionable device, the partition number of a device. */ diff --git a/contrib/platform/src/com/sun/jna/platform/win32/WinioctlUtil.java b/contrib/platform/src/com/sun/jna/platform/win32/WinioctlUtil.java new file mode 100644 index 0000000000..5475dde622 --- /dev/null +++ b/contrib/platform/src/com/sun/jna/platform/win32/WinioctlUtil.java @@ -0,0 +1,85 @@ +/* Copyright (c) 2016 Adam Marcionek, All Rights Reserved + * + * The contents of this file is dual-licensed under 2 + * alternative Open Source/Free licenses: LGPL 2.1 or later and + * Apache License 2.0. (starting with JNA version 4.0.0). + * + * You can freely decide which license you want to apply to + * the project. + * + * You may obtain a copy of the LGPL License at: + * + * http://www.gnu.org/licenses/licenses.html + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "LGPL2.1". + * + * You may obtain a copy of the Apache License at: + * + * http://www.apache.org/licenses/ + * + * A copy is also included in the downloadable source code package + * containing JNA, in file "AL2.0". + */ +package com.sun.jna.platform.win32; + +/** + * Winioctl Utility API. Use WinioctlFunction to construct the full control codes for the + * FSCTL_* functions defined in Winioctl.h + * + * @author amarcionek[at]gmail.com + */ +public abstract class WinioctlUtil { + + /** + * Simulates the macro CTL_CODE from Winioctl.h + * @param DeviceType the device type + * @param Function the function + * @param Method the method + * @param Access the access + * @return int with the resulting control code + */ + public static int CTL_CODE(int DeviceType, int Function, int Method, int Access) { + return ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method); + } + + /** + * Base interface for a Winiotcl function used to construct the control code + */ + public interface WinioctlFunction { + /** + * @return the control code given the IOTCL's parameters + */ + public abstract int getControlCode(); + } + + public static class FSCTL_GET_COMPRESSION implements WinioctlFunction { + public int getControlCode() { + return WinioctlUtil.CTL_CODE(Winioctl.FILE_DEVICE_FILE_SYSTEM, Winioctl.FSCTL_GET_COMPRESSION, Winioctl.METHOD_BUFFERED, Winioctl.FILE_ANY_ACCESS); + } + } + + public static class FSCTL_SET_COMPRESSION implements WinioctlFunction { + public int getControlCode() { + return WinioctlUtil.CTL_CODE(Winioctl.FILE_DEVICE_FILE_SYSTEM, Winioctl.FSCTL_SET_COMPRESSION, Winioctl.METHOD_BUFFERED, WinNT.FILE_READ_DATA | WinNT.FILE_WRITE_DATA); + } + } + + public static class FSCTL_SET_REPARSE_POINT implements WinioctlFunction { + public int getControlCode() { + return WinioctlUtil.CTL_CODE(Winioctl.FILE_DEVICE_FILE_SYSTEM, Winioctl.FSCTL_SET_REPARSE_POINT, Winioctl.METHOD_BUFFERED, Winioctl.FILE_SPECIAL_ACCESS); + } + } + + public static class FSCTL_GET_REPARSE_POINT implements WinioctlFunction { + public int getControlCode() { + return WinioctlUtil.CTL_CODE(Winioctl.FILE_DEVICE_FILE_SYSTEM, Winioctl.FSCTL_GET_REPARSE_POINT, Winioctl.METHOD_BUFFERED, Winioctl.FILE_ANY_ACCESS); + } + } + + public static class FSCTL_DELETE_REPARSE_POINT implements WinioctlFunction { + public int getControlCode() { + return WinioctlUtil.CTL_CODE(Winioctl.FILE_DEVICE_FILE_SYSTEM, Winioctl.FSCTL_DELETE_REPARSE_POINT, Winioctl.METHOD_BUFFERED, Winioctl.FILE_SPECIAL_ACCESS); + } + } +} \ No newline at end of file diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java index 21b40f18f2..6a60cdeb4c 100755 --- a/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Advapi32UtilTest.java @@ -24,6 +24,7 @@ import com.sun.jna.platform.win32.Advapi32Util.Account; import com.sun.jna.platform.win32.Advapi32Util.EventLogIterator; import com.sun.jna.platform.win32.Advapi32Util.EventLogRecord; +import com.sun.jna.platform.win32.Advapi32Util.Privilege; import com.sun.jna.platform.win32.LMAccess.USER_INFO_1; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; @@ -599,6 +600,35 @@ public void testBackupEncryptedFile() throws Exception { dest.delete(); } + /** + * Test Privilege class + */ + public void testPrivilege() { + // Test multiple known privileges + Privilege privilege = new Privilege(WinNT.SE_ASSIGNPRIMARYTOKEN_NAME, WinNT.SE_BACKUP_NAME); + try { + privilege.enable(); + // Will throw if it fails p.enable() fails + } + finally { + privilege.close(); + } + + // Test unknown privilege + try { + privilege = new Privilege("NOT_A_PRIVILEGE"); + } + catch (IllegalArgumentException ex) { + // Exception is expected + } + catch (Exception ex) { + fail("Encountered unknown exception - " + ex.getMessage()); + } + finally { + privilege.close(); + } + } + private File createTempFile() throws Exception{ String filePath = System.getProperty("java.io.tmpdir") + System.nanoTime() + ".text"; diff --git a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java index e993bbd404..b0ca34cf51 100644 --- a/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java +++ b/contrib/platform/test/com/sun/jna/platform/win32/Kernel32Test.java @@ -12,7 +12,6 @@ */ package com.sun.jna.platform.win32; -import com.sun.jna.Function; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; @@ -21,6 +20,10 @@ import java.io.IOException; import java.io.PrintWriter; import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.attribute.FileTime; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; @@ -29,6 +32,7 @@ import java.util.List; import java.util.TimeZone; +import com.sun.jna.Function; import com.sun.jna.Memory; import com.sun.jna.Native; import com.sun.jna.NativeLibrary; @@ -36,18 +40,34 @@ import com.sun.jna.Platform; import com.sun.jna.Pointer; import com.sun.jna.platform.win32.BaseTSD.SIZE_T; +import com.sun.jna.platform.win32.Ntifs.REPARSE_DATA_BUFFER; +import com.sun.jna.platform.win32.Ntifs.SymbolicLinkReparseBuffer; import com.sun.jna.platform.win32.WinBase.FILETIME; +import com.sun.jna.platform.win32.WinBase.FILE_ATTRIBUTE_TAG_INFO; +import com.sun.jna.platform.win32.WinBase.FILE_BASIC_INFO; +import com.sun.jna.platform.win32.WinBase.FILE_COMPRESSION_INFO; +import com.sun.jna.platform.win32.WinBase.FILE_DISPOSITION_INFO; +import com.sun.jna.platform.win32.WinBase.FILE_ID_INFO; +import com.sun.jna.platform.win32.WinBase.FILE_STANDARD_INFO; import com.sun.jna.platform.win32.WinBase.MEMORYSTATUSEX; +import com.sun.jna.platform.win32.WinBase.SYSTEMTIME; import com.sun.jna.platform.win32.WinBase.SYSTEM_INFO; +import com.sun.jna.platform.win32.WinBase.WIN32_FIND_DATA; import com.sun.jna.platform.win32.WinDef.DWORD; import com.sun.jna.platform.win32.WinDef.HMODULE; import com.sun.jna.platform.win32.WinDef.HWND; +import com.sun.jna.platform.win32.WinDef.USHORT; import com.sun.jna.platform.win32.WinNT.HANDLE; import com.sun.jna.platform.win32.WinNT.HANDLEByReference; import com.sun.jna.platform.win32.WinNT.MEMORY_BASIC_INFORMATION; import com.sun.jna.platform.win32.WinNT.OSVERSIONINFO; import com.sun.jna.platform.win32.WinNT.OSVERSIONINFOEX; +import com.sun.jna.platform.win32.WinioctlUtil.FSCTL_GET_COMPRESSION; +import com.sun.jna.platform.win32.WinioctlUtil.FSCTL_GET_REPARSE_POINT; +import com.sun.jna.platform.win32.WinioctlUtil.FSCTL_SET_COMPRESSION; +import com.sun.jna.platform.win32.WinioctlUtil.FSCTL_SET_REPARSE_POINT; import com.sun.jna.ptr.IntByReference; +import com.sun.jna.ptr.ShortByReference; import junit.framework.TestCase; @@ -554,6 +574,131 @@ public void testGetFileAttributes() { assertTrue(WinBase.INVALID_FILE_ATTRIBUTES != Kernel32.INSTANCE.GetFileAttributes(".")); } + public void testDeviceIoControlFsctlCompression() throws IOException { + File tmp = File.createTempFile("testDeviceIoControlFsctlCompression", "jna"); + tmp.deleteOnExit(); + + HANDLE hFile = Kernel32.INSTANCE.CreateFile(tmp.getAbsolutePath(), WinNT.GENERIC_ALL, WinNT.FILE_SHARE_READ, + new WinBase.SECURITY_ATTRIBUTES(), WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null); + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + ShortByReference lpBuffer = new ShortByReference(); + IntByReference lpBytes = new IntByReference(); + + if (false == Kernel32.INSTANCE.DeviceIoControl(hFile, + new FSCTL_GET_COMPRESSION().getControlCode(), + null, + 0, + lpBuffer.getPointer(), + USHORT.SIZE, + lpBytes, + null)) { + fail("DeviceIoControl failed with " + Kernel32.INSTANCE.GetLastError()); + } + assertEquals(WinNT.COMPRESSION_FORMAT_NONE, lpBuffer.getValue()); + assertEquals(USHORT.SIZE, lpBytes.getValue()); + + lpBuffer = new ShortByReference((short)WinNT.COMPRESSION_FORMAT_LZNT1); + + if (false == Kernel32.INSTANCE.DeviceIoControl(hFile, + new FSCTL_SET_COMPRESSION().getControlCode(), + lpBuffer.getPointer(), + USHORT.SIZE, + null, + 0, + lpBytes, + null)) { + fail("DeviceIoControl failed with " + Kernel32.INSTANCE.GetLastError()); + } + + if (false == Kernel32.INSTANCE.DeviceIoControl(hFile, + new FSCTL_GET_COMPRESSION().getControlCode(), + null, + 0, + lpBuffer.getPointer(), + USHORT.SIZE, + lpBytes, + null)) { + fail("DeviceIoControl failed with " + Kernel32.INSTANCE.GetLastError()); + } + assertEquals(WinNT.COMPRESSION_FORMAT_LZNT1, lpBuffer.getValue()); + assertEquals(USHORT.SIZE, lpBytes.getValue()); + + } finally { + Kernel32Util.closeHandle(hFile); + } + } + + /** + * NOTE: Due to process elevation, this test must be run as administrator + * @throws IOException + */ + public void testDeviceIoControlFsctlReparse() throws IOException { + Path folder = Files.createTempDirectory("testDeviceIoControlFsctlReparse_FOLDER"); + Path link = Files.createTempDirectory("testDeviceIoControlFsctlReparse_LINK"); + File delFolder = folder.toFile(); + delFolder.deleteOnExit(); + File delLink = link.toFile(); + delLink.deleteOnExit(); + + // Required for FSCTL_SET_REPARSE_POINT + Advapi32Util.Privilege restore = new Advapi32Util.Privilege(WinNT.SE_RESTORE_NAME); + try { + restore.enable(); + HANDLE hFile = Kernel32.INSTANCE.CreateFile(link.toAbsolutePath().toString(), + WinNT.GENERIC_READ | WinNT.FILE_WRITE_ATTRIBUTES | WinNT.FILE_WRITE_EA, + WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE | WinNT.FILE_SHARE_DELETE, + new WinBase.SECURITY_ATTRIBUTES(), + WinNT.OPEN_EXISTING, + WinNT.FILE_ATTRIBUTE_DIRECTORY | WinNT.FILE_FLAG_BACKUP_SEMANTICS | WinNT.FILE_FLAG_OPEN_REPARSE_POINT, + null); + + if (WinBase.INVALID_HANDLE_VALUE.equals(hFile)) { + fail("CreateFile failed with " + Kernel32.INSTANCE.GetLastError()); + } + + try { + SymbolicLinkReparseBuffer symLinkReparseBuffer = new SymbolicLinkReparseBuffer(folder.getFileName().toString(), + folder.getFileName().toString(), + Ntifs.SYMLINK_FLAG_RELATIVE); + + REPARSE_DATA_BUFFER lpBuffer = new REPARSE_DATA_BUFFER(WinNT.IO_REPARSE_TAG_SYMLINK, (short) 0, symLinkReparseBuffer); + + assertTrue(Kernel32.INSTANCE.DeviceIoControl(hFile, + new FSCTL_SET_REPARSE_POINT().getControlCode(), + lpBuffer.getPointer(), + lpBuffer.getSize(), + null, + 0, + null, + null)); + + Memory p = new Memory(REPARSE_DATA_BUFFER.sizeOf()); + IntByReference lpBytes = new IntByReference(); + assertTrue(Kernel32.INSTANCE.DeviceIoControl(hFile, + new FSCTL_GET_REPARSE_POINT().getControlCode(), + null, + 0, + p, + (int) p.size(), + lpBytes, + null)); + // Is a reparse point + lpBuffer = new REPARSE_DATA_BUFFER(p); + assertTrue(lpBytes.getValue() > 0); + assertTrue(lpBuffer.ReparseTag == WinNT.IO_REPARSE_TAG_SYMLINK); + assertEquals(folder.getFileName().toString(), lpBuffer.u.symLinkReparseBuffer.getPrintName()); + assertEquals(folder.getFileName().toString(), lpBuffer.u.symLinkReparseBuffer.getSubstituteName()); + } finally { + Kernel32Util.closeHandle(hFile); + } + } + finally { + restore.close(); + } + } + public void testCopyFile() throws IOException { File source = File.createTempFile("testCopyFile", "jna"); source.deleteOnExit(); @@ -606,6 +751,239 @@ public void testCreateProcess() { assertTrue(processInformation.dwProcessId.longValue() > 0); } + public void testFindFirstFile() throws IOException { + Path tmpDir = Files.createTempDirectory("testFindFirstFile"); + File tmpFile = new File(Files.createTempFile(tmpDir, "testFindFirstFile", ".jna").toString()); + + Memory p = new Memory(WIN32_FIND_DATA.sizeOf()); + HANDLE hFile = Kernel32.INSTANCE.FindFirstFile(tmpDir.toAbsolutePath().toString() + "\\*", p); + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + + // Get data and confirm the 1st name is . for the directory itself. + WIN32_FIND_DATA fd = new WIN32_FIND_DATA(p); + String actualFileName = new String(fd.getFileName()); + assertTrue(actualFileName.contentEquals(".")); + + // Get data and confirm the 2nd name is .. for the directory's parent + assertTrue(Kernel32.INSTANCE.FindNextFile(hFile, p)); + fd = new WIN32_FIND_DATA(p); + actualFileName = new String(fd.getFileName()); + assertTrue(actualFileName.contentEquals("..")); + + // Get data and confirm the 3rd name is the tmp file name + assertTrue(Kernel32.INSTANCE.FindNextFile(hFile, p)); + fd = new WIN32_FIND_DATA(p); + actualFileName = new String(fd.getFileName()); + assertTrue(actualFileName.contentEquals(tmpFile.getName())); + + // No more files in directory + assertFalse(Kernel32.INSTANCE.FindNextFile(hFile, p)); + assertEquals(WinNT.ERROR_NO_MORE_FILES, Kernel32.INSTANCE.GetLastError()); + } + finally { + Kernel32.INSTANCE.FindClose(hFile); + tmpFile.delete(); + Files.delete(tmpDir); + } + } + + public void testFindFirstFileExFindExInfoStandard() throws IOException { + Path tmpDir = Files.createTempDirectory("testFindFirstFileExFindExInfoStandard"); + File tmpFile = new File(Files.createTempFile(tmpDir, "testFindFirstFileExFindExInfoStandard", ".jna").toString()); + + Memory p = new Memory(WIN32_FIND_DATA.sizeOf()); + HANDLE hFile = Kernel32.INSTANCE.FindFirstFileEx(tmpDir.toAbsolutePath().toString() + "\\*", + WinBase.FindExInfoStandard, + p, + WinBase.FindExSearchNameMatch, + null, + new DWORD(0)); + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + + // Get data and confirm the 1st name is . for the directory itself. + WIN32_FIND_DATA fd = new WIN32_FIND_DATA(p); + String actualFileName = new String(fd.getFileName()); + assertTrue(actualFileName.contentEquals(".")); + + // Get data and confirm the 2nd name is .. for the directory's parent + assertTrue(Kernel32.INSTANCE.FindNextFile(hFile, p)); + fd = new WIN32_FIND_DATA(p); + actualFileName = new String(fd.getFileName()); + assertTrue(actualFileName.contentEquals("..")); + + // Get data and confirm the 3rd name is the tmp file name + assertTrue(Kernel32.INSTANCE.FindNextFile(hFile, p)); + fd = new WIN32_FIND_DATA(p); + actualFileName = new String(fd.getFileName()); + assertTrue(actualFileName.contentEquals(tmpFile.getName())); + + // No more files in directory + assertFalse(Kernel32.INSTANCE.FindNextFile(hFile, p)); + assertEquals(WinNT.ERROR_NO_MORE_FILES, Kernel32.INSTANCE.GetLastError()); + } + finally { + Kernel32.INSTANCE.FindClose(hFile); + tmpFile.delete(); + Files.delete(tmpDir); + } + } + + public void testFindFirstFileExFindExInfoBasic() throws IOException { + Path tmpDir = Files.createTempDirectory("testFindFirstFileExFindExInfoBasic"); + File tmpFile = new File(Files.createTempFile(tmpDir, "testFindFirstFileExFindExInfoBasic", ".jna").toString()); + + Memory p = new Memory(WIN32_FIND_DATA.sizeOf()); + // Add the file name to the search to get just that one entry + HANDLE hFile = Kernel32.INSTANCE.FindFirstFileEx(tmpDir.toAbsolutePath().toString() + "\\" + tmpFile.getName(), + WinBase.FindExInfoBasic, + p, + WinBase.FindExSearchNameMatch, + null, + new DWORD(0)); + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + // Get data and confirm the 1st name is for the file itself + WIN32_FIND_DATA fd = new WIN32_FIND_DATA(p); + String actualFileName = new String(fd.getFileName()); + actualFileName = new String(fd.getFileName()); + assertTrue(actualFileName.contentEquals(tmpFile.getName())); + + // FindExInfoBasic does not return the short name, so confirm that its empty + String alternateFileName = fd.getAlternateFileName(); + assertTrue(alternateFileName.isEmpty()); + + // No more files in directory + assertFalse(Kernel32.INSTANCE.FindNextFile(hFile, p)); + assertEquals(WinNT.ERROR_NO_MORE_FILES, Kernel32.INSTANCE.GetLastError()); + } + finally { + Kernel32.INSTANCE.FindClose(hFile); + tmpFile.delete(); + Files.delete(tmpDir); + } + } + + public void testGetFileInformationByHandleEx() throws IOException { + File tmp = File.createTempFile("testGetFileInformationByHandleEx", "jna"); + tmp.deleteOnExit(); + + HANDLE hFile = Kernel32.INSTANCE.CreateFile(tmp.getAbsolutePath(), WinNT.GENERIC_WRITE, WinNT.FILE_SHARE_WRITE, + new WinBase.SECURITY_ATTRIBUTES(), WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null); + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + + Memory p = new Memory(FILE_BASIC_INFO.sizeOf()); + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileBasicInfo, p, new DWORD(p.size()))) { + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + } + FILE_BASIC_INFO fbi = new FILE_BASIC_INFO(p); + // New file has non-zero creation time + assertTrue(0 != fbi.CreationTime.getValue()); + + p = new Memory(FILE_STANDARD_INFO.sizeOf()); + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileStandardInfo, p, new DWORD(p.size()))) { + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + } + FILE_STANDARD_INFO fsi = new FILE_STANDARD_INFO(p); + // New file has 1 link + assertEquals(1, fsi.NumberOfLinks); + + p = new Memory(FILE_COMPRESSION_INFO.sizeOf()); + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileCompressionInfo, p, new DWORD(p.size()))) { + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + } + FILE_COMPRESSION_INFO fci = new FILE_COMPRESSION_INFO(p); + // Uncompressed file should be zero + assertEquals(0, fci.CompressionFormat); + + p = new Memory(FILE_ATTRIBUTE_TAG_INFO.sizeOf()); + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileAttributeTagInfo, p, new DWORD(p.size()))) { + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + } + FILE_ATTRIBUTE_TAG_INFO fati = new FILE_ATTRIBUTE_TAG_INFO(p); + // New files have the archive bit + assertEquals(WinNT.FILE_ATTRIBUTE_ARCHIVE, fati.FileAttributes); + + p = new Memory(FILE_ID_INFO.sizeOf()); + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileIdInfo, p, new DWORD(p.size()))) { + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + } + FILE_ID_INFO fii = new FILE_ID_INFO(p); + // Volume serial number should be non-zero + assertFalse(fii.VolumeSerialNumber == 0); + } finally { + Kernel32.INSTANCE.CloseHandle(hFile); + } + } + + public void testSetFileInformationByHandleFileBasicInfo() throws IOException, InterruptedException { + File tmp = File.createTempFile("testSetFileInformationByHandleFileBasicInfo", "jna"); + tmp.deleteOnExit(); + + HANDLE hFile = Kernel32.INSTANCE.CreateFile(tmp.getAbsolutePath(), + WinNT.GENERIC_READ | WinNT.GENERIC_WRITE, + WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE, + new WinBase.SECURITY_ATTRIBUTES(), + WinNT.OPEN_EXISTING, + WinNT.FILE_ATTRIBUTE_NORMAL, + null); + + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + Memory p = new Memory(FILE_BASIC_INFO.sizeOf()); + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileBasicInfo, p, new DWORD(p.size()))) + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + + FILE_BASIC_INFO fbi = new FILE_BASIC_INFO(p); + // Add TEMP attribute + fbi.FileAttributes = fbi.FileAttributes | WinNT.FILE_ATTRIBUTE_TEMPORARY; + fbi.ChangeTime = new WinNT.LARGE_INTEGER(0); + fbi.CreationTime = new WinNT.LARGE_INTEGER(0); + fbi.LastAccessTime = new WinNT.LARGE_INTEGER(0); + fbi.LastWriteTime = new WinNT.LARGE_INTEGER(0); + fbi.write(); + + if (false == Kernel32.INSTANCE.SetFileInformationByHandle(hFile, WinBase.FileBasicInfo, fbi.getPointer(), new DWORD(FILE_BASIC_INFO.sizeOf()))) + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileBasicInfo, p, new DWORD(p.size()))) + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + + fbi = new FILE_BASIC_INFO(p); + assertTrue((fbi.FileAttributes & WinNT.FILE_ATTRIBUTE_TEMPORARY) != 0); + } + finally { + Kernel32.INSTANCE.CloseHandle(hFile); + } + } + + public void testSetFileInformationByHandleFileDispositionInfo() throws IOException, InterruptedException { + File tmp = File.createTempFile("testSetFileInformationByHandleFileDispositionInfo", "jna"); + + HANDLE hFile = Kernel32.INSTANCE.CreateFile(tmp.getAbsolutePath(), WinNT.GENERIC_WRITE | WinNT.DELETE, WinNT.FILE_SHARE_WRITE, + new WinBase.SECURITY_ATTRIBUTES(), WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null); + + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + FILE_DISPOSITION_INFO fdi = new FILE_DISPOSITION_INFO(true); + if (false == Kernel32.INSTANCE.SetFileInformationByHandle(hFile, WinBase.FileDispositionInfo, fdi.getPointer(), new DWORD(FILE_DISPOSITION_INFO.sizeOf()))) + fail("SetFileInformationByHandle failed with " + Kernel32.INSTANCE.GetLastError()); + + } finally { + Kernel32.INSTANCE.CloseHandle(hFile); + } + + assertFalse(Files.exists(Paths.get(tmp.getAbsolutePath()))); + } + public void testGetSetFileTime() throws IOException { File tmp = File.createTempFile("testGetSetFileTime", "jna"); tmp.deleteOnExit(); @@ -774,6 +1152,72 @@ public final void testWritePrivateProfileSection() throws IOException { reader.close(); } + /** + * Test both SystemTimeToFileTime and FileTimeToSystemTime + * @throws IOException + */ + public final void testSystemTimeToFileTimeAndFileTimeToSystemTime() throws IOException { + + WinBase.SYSTEMTIME systemTime = new WinBase.SYSTEMTIME(); + Kernel32.INSTANCE.GetSystemTime(systemTime); + WinBase.FILETIME fileTime = new WinBase.FILETIME(); + + if (false == Kernel32.INSTANCE.SystemTimeToFileTime(systemTime, fileTime)) { + fail("SystemTimeToFileTime failed with " + Kernel32.INSTANCE.GetLastError()); + } + + WinBase.SYSTEMTIME newSystemTime = new WinBase.SYSTEMTIME(); + if (false == Kernel32.INSTANCE.FileTimeToSystemTime(fileTime, newSystemTime)) { + fail("FileTimeToSystemTime failed with " + Kernel32.INSTANCE.GetLastError()); + } + + assertEquals(systemTime.wYear, newSystemTime.wYear); + assertEquals(systemTime.wDay, newSystemTime.wDay); + assertEquals(systemTime.wMonth, newSystemTime.wMonth); + assertEquals(systemTime.wHour, newSystemTime.wHour); + assertEquals(systemTime.wMinute, newSystemTime.wMinute); + assertEquals(systemTime.wSecond, newSystemTime.wSecond); + assertEquals(systemTime.wMilliseconds, newSystemTime.wMilliseconds); + } + + /** + * Test FILETIME's LARGE_INTEGER constructor + * @throws IOException + */ + public final void testFileTimeFromLargeInteger() throws IOException { + + File tmp = File.createTempFile("testGetFileInformationByHandleEx", "jna"); + tmp.deleteOnExit(); + + HANDLE hFile = Kernel32.INSTANCE.CreateFile(tmp.getAbsolutePath(), WinNT.GENERIC_WRITE, WinNT.FILE_SHARE_WRITE, + new WinBase.SECURITY_ATTRIBUTES(), WinNT.OPEN_EXISTING, WinNT.FILE_ATTRIBUTE_NORMAL, null); + assertFalse(WinBase.INVALID_HANDLE_VALUE.equals(hFile)); + + try { + + Memory p = new Memory(FILE_BASIC_INFO.sizeOf()); + if (false == Kernel32.INSTANCE.GetFileInformationByHandleEx(hFile, WinBase.FileBasicInfo, p, new DWORD(p.size()))) { + fail("GetFileInformationByHandleEx failed with " + Kernel32.INSTANCE.GetLastError()); + } + FILE_BASIC_INFO fbi = new FILE_BASIC_INFO(p); + FILETIME ft = new FILETIME(fbi.LastWriteTime); + SYSTEMTIME stUTC = new SYSTEMTIME(); + SYSTEMTIME stLocal = new SYSTEMTIME(); + Kernel32.INSTANCE.FileTimeToSystemTime(ft, stUTC); + // Covert to local + Kernel32.INSTANCE.SystemTimeToTzSpecificLocalTime(null, stUTC, stLocal); + FileTime calculatedCreateTime = FileTime.fromMillis(stLocal.toCalendar().getTimeInMillis()); + + // Actual file's createTime + FileTime createTime = Files.getLastModifiedTime(Paths.get(tmp.getAbsolutePath())); + + assertEquals(createTime.toMillis(), calculatedCreateTime.toMillis()); + } + finally { + Kernel32.INSTANCE.CloseHandle(hFile); + } + } + public final void testCreateRemoteThread() throws IOException { HANDLE hThrd = Kernel32.INSTANCE.CreateRemoteThread(null, null, 0, null, null, null, null); assertNull(hThrd);