58
58
#define FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE 0x0010
59
59
#endif /* FILE_DISPOSITION_IGNORE_READONLY_ATTRIBUTE */
60
60
61
+ NTSTATUS uv__RtlUnicodeStringInit (
62
+ PUNICODE_STRING DestinationString ,
63
+ PWSTR SourceString ,
64
+ size_t SourceStringLen
65
+ ) {
66
+ if (SourceStringLen > 0x7FFF )
67
+ return STATUS_INVALID_PARAMETER ;
68
+ DestinationString -> MaximumLength = DestinationString -> Length =
69
+ SourceStringLen * sizeof (SourceString [0 ]);
70
+ DestinationString -> Buffer = SourceString ;
71
+ return STATUS_SUCCESS ;
72
+ }
73
+
61
74
#define INIT (subtype ) \
62
75
do { \
63
76
if (req == NULL) \
@@ -1689,12 +1702,12 @@ INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
1689
1702
uv_stat_t * statbuf , int do_lstat ) {
1690
1703
FILE_STAT_BASIC_INFORMATION stat_info ;
1691
1704
1692
- // Check if the new fast API is available.
1705
+ /* Check if the new fast API is available. */
1693
1706
if (!pGetFileInformationByName ) {
1694
1707
return FS__STAT_PATH_TRY_SLOW ;
1695
1708
}
1696
1709
1697
- // Check if the API call fails.
1710
+ /* Check if the API call fails. */
1698
1711
if (!pGetFileInformationByName (path , FileStatBasicByNameInfo , & stat_info ,
1699
1712
sizeof (stat_info ))) {
1700
1713
switch (GetLastError ()) {
@@ -1708,7 +1721,7 @@ INLINE static fs__stat_path_return_t fs__stat_path(WCHAR* path,
1708
1721
return FS__STAT_PATH_TRY_SLOW ;
1709
1722
}
1710
1723
1711
- // A file handle is needed to get st_size for links.
1724
+ /* A file handle is needed to get st_size for links. */
1712
1725
if ((stat_info .FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT )) {
1713
1726
return FS__STAT_PATH_TRY_SLOW ;
1714
1727
}
@@ -1802,7 +1815,6 @@ INLINE static int fs__stat_handle(HANDLE handle, uv_stat_t* statbuf,
1802
1815
* detect this failure and retry without do_lstat if appropriate.
1803
1816
*/
1804
1817
if (fs__readlink_handle (handle , NULL , & target_length ) != 0 ) {
1805
- fs__stat_assign_statbuf (statbuf , stat_info , do_lstat );
1806
1818
return -1 ;
1807
1819
}
1808
1820
stat_info .EndOfFile .QuadPart = target_length ;
@@ -1941,6 +1953,179 @@ INLINE static void fs__stat_prepare_path(WCHAR* pathw) {
1941
1953
}
1942
1954
}
1943
1955
1956
+ INLINE static DWORD fs__stat_directory (WCHAR * path , uv_stat_t * statbuf ,
1957
+ int do_lstat , DWORD ret_error ) {
1958
+ HANDLE handle = INVALID_HANDLE_VALUE ;
1959
+ FILE_STAT_BASIC_INFORMATION stat_info ;
1960
+ FILE_ID_FULL_DIR_INFORMATION dir_info ;
1961
+ FILE_FS_VOLUME_INFORMATION volume_info ;
1962
+ FILE_FS_DEVICE_INFORMATION device_info ;
1963
+ IO_STATUS_BLOCK io_status ;
1964
+ NTSTATUS nt_status ;
1965
+ WCHAR * path_dirpath = NULL ;
1966
+ WCHAR * path_filename = NULL ;
1967
+ UNICODE_STRING FileMask ;
1968
+ size_t len ;
1969
+ size_t split ;
1970
+ WCHAR splitchar ;
1971
+ int includes_name ;
1972
+
1973
+ /* AKA strtok or wcscspn, in reverse. */
1974
+ len = wcslen (path );
1975
+ split = len ;
1976
+
1977
+ includes_name = 0 ;
1978
+ while (split > 0 && path [split - 1 ] != L'\\' && path [split - 1 ] != L'/' &&
1979
+ path [split - 1 ] != L':' ) {
1980
+ /* check if the path contains a character other than /,\,:,. */
1981
+ if (path [split - 1 ] != '.' ) {
1982
+ includes_name = 1 ;
1983
+ }
1984
+ split -- ;
1985
+ }
1986
+ /* If the path is a relative path with a file name or a folder name */
1987
+ if (split == 0 && includes_name ) {
1988
+ path_dirpath = L"." ;
1989
+ /* If there is a slash or a backslash */
1990
+ } else if (path [split - 1 ] == L'\\' || path [split - 1 ] == L'/' ) {
1991
+ path_dirpath = path ;
1992
+ /* If there is no filename, consider it as a relative folder path */
1993
+ if (!includes_name ) {
1994
+ split = len ;
1995
+ /* Else, split it */
1996
+ } else {
1997
+ splitchar = path [split - 1 ];
1998
+ path [split - 1 ] = L'\0' ;
1999
+ }
2000
+ /* e.g. "..", "c:" */
2001
+ } else {
2002
+ path_dirpath = path ;
2003
+ split = len ;
2004
+ }
2005
+ path_filename = & path [split ];
2006
+
2007
+ len = 0 ;
2008
+ while (1 ) {
2009
+ if (path_filename [len ] == L'\0' )
2010
+ break ;
2011
+ if (path_filename [len ] == L'*' || path_filename [len ] == L'?' ||
2012
+ path_filename [len ] == L'>' || path_filename [len ] == L'<' ||
2013
+ path_filename [len ] == L'"' ) {
2014
+ ret_error = ERROR_INVALID_NAME ;
2015
+ goto cleanup ;
2016
+ }
2017
+ len ++ ;
2018
+ }
2019
+
2020
+ /* Get directory handle */
2021
+ handle = CreateFileW (path_dirpath ,
2022
+ FILE_LIST_DIRECTORY ,
2023
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
2024
+ NULL ,
2025
+ OPEN_EXISTING ,
2026
+ FILE_FLAG_BACKUP_SEMANTICS ,
2027
+ NULL );
2028
+
2029
+ if (handle == INVALID_HANDLE_VALUE ) {
2030
+ ret_error = GetLastError ();
2031
+ goto cleanup ;
2032
+ }
2033
+
2034
+ /* Get files in the directory */
2035
+ nt_status = uv__RtlUnicodeStringInit (& FileMask , path_filename , len );
2036
+ if (!NT_SUCCESS (nt_status )) {
2037
+ ret_error = pRtlNtStatusToDosError (nt_status );
2038
+ goto cleanup ;
2039
+ }
2040
+ nt_status = pNtQueryDirectoryFile (handle ,
2041
+ NULL ,
2042
+ NULL ,
2043
+ NULL ,
2044
+ & io_status ,
2045
+ & dir_info ,
2046
+ sizeof (dir_info ),
2047
+ FileIdFullDirectoryInformation ,
2048
+ TRUE,
2049
+ & FileMask ,
2050
+ TRUE);
2051
+
2052
+ /* Buffer overflow (a warning status code) is expected here since there isn't
2053
+ * enough space to store the FileName, and actually indicates success. */
2054
+ if (!NT_SUCCESS (nt_status ) && nt_status != STATUS_BUFFER_OVERFLOW ) {
2055
+ if (nt_status == STATUS_NO_MORE_FILES )
2056
+ ret_error = ERROR_PATH_NOT_FOUND ;
2057
+ else
2058
+ ret_error = pRtlNtStatusToDosError (nt_status );
2059
+ goto cleanup ;
2060
+ }
2061
+
2062
+ /* Assign values to stat_info */
2063
+ memset (& stat_info , 0 , sizeof (FILE_STAT_BASIC_INFORMATION ));
2064
+ stat_info .FileAttributes = dir_info .FileAttributes ;
2065
+ stat_info .CreationTime .QuadPart = dir_info .CreationTime .QuadPart ;
2066
+ stat_info .LastAccessTime .QuadPart = dir_info .LastAccessTime .QuadPart ;
2067
+ stat_info .LastWriteTime .QuadPart = dir_info .LastWriteTime .QuadPart ;
2068
+ if (stat_info .FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT ) {
2069
+ /* A file handle is needed to get st_size for the link (from
2070
+ * FSCTL_GET_REPARSE_POINT), which is required by posix, but we are here
2071
+ * because getting the file handle failed. We could get just the
2072
+ * ReparsePointTag by querying FILE_ID_EXTD_DIR_INFORMATION instead to make
2073
+ * sure this really is a link before giving up here on the uv_fs_stat call,
2074
+ * but that doesn't seem essential. */
2075
+ if (!do_lstat )
2076
+ goto cleanup ;
2077
+ stat_info .EndOfFile .QuadPart = 0 ;
2078
+ stat_info .AllocationSize .QuadPart = 0 ;
2079
+ } else {
2080
+ stat_info .EndOfFile .QuadPart = dir_info .EndOfFile .QuadPart ;
2081
+ stat_info .AllocationSize .QuadPart = dir_info .AllocationSize .QuadPart ;
2082
+ }
2083
+ stat_info .ChangeTime .QuadPart = dir_info .ChangeTime .QuadPart ;
2084
+ stat_info .FileId .QuadPart = dir_info .FileId .QuadPart ;
2085
+
2086
+ /* Finish up by getting device info from the directory handle,
2087
+ * since files presumably must live on their device. */
2088
+ nt_status = pNtQueryVolumeInformationFile (handle ,
2089
+ & io_status ,
2090
+ & volume_info ,
2091
+ sizeof volume_info ,
2092
+ FileFsVolumeInformation );
2093
+
2094
+ /* Buffer overflow (a warning status code) is expected here. */
2095
+ if (io_status .Status == STATUS_NOT_IMPLEMENTED ) {
2096
+ stat_info .VolumeSerialNumber .QuadPart = 0 ;
2097
+ } else if (NT_ERROR (nt_status )) {
2098
+ ret_error = pRtlNtStatusToDosError (nt_status );
2099
+ goto cleanup ;
2100
+ } else {
2101
+ stat_info .VolumeSerialNumber .QuadPart = volume_info .VolumeSerialNumber ;
2102
+ }
2103
+
2104
+ nt_status = pNtQueryVolumeInformationFile (handle ,
2105
+ & io_status ,
2106
+ & device_info ,
2107
+ sizeof device_info ,
2108
+ FileFsDeviceInformation );
2109
+
2110
+ /* Buffer overflow (a warning status code) is expected here. */
2111
+ if (NT_ERROR (nt_status )) {
2112
+ ret_error = pRtlNtStatusToDosError (nt_status );
2113
+ goto cleanup ;
2114
+ }
2115
+
2116
+ stat_info .DeviceType = device_info .DeviceType ;
2117
+ stat_info .NumberOfLinks = 1 ; /* No way to recover this info. */
2118
+
2119
+ fs__stat_assign_statbuf (statbuf , stat_info , do_lstat );
2120
+ ret_error = 0 ;
2121
+
2122
+ cleanup :
2123
+ if (split != 0 )
2124
+ path [split - 1 ] = splitchar ;
2125
+ if (handle != INVALID_HANDLE_VALUE )
2126
+ CloseHandle (handle );
2127
+ return ret_error ;
2128
+ }
1944
2129
1945
2130
INLINE static DWORD fs__stat_impl_from_path (WCHAR * path ,
1946
2131
int do_lstat ,
@@ -1949,7 +2134,7 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
1949
2134
DWORD flags ;
1950
2135
DWORD ret ;
1951
2136
1952
- // If new API exists, try to use it.
2137
+ /* If new API exists, try to use it. */
1953
2138
switch (fs__stat_path (path , statbuf , do_lstat )) {
1954
2139
case FS__STAT_PATH_SUCCESS :
1955
2140
return 0 ;
@@ -1959,7 +2144,7 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
1959
2144
break ;
1960
2145
}
1961
2146
1962
- // If the new API does not exist, use the old API.
2147
+ /* If the new API does not exist, use the old API. */
1963
2148
flags = FILE_FLAG_BACKUP_SEMANTICS ;
1964
2149
if (do_lstat )
1965
2150
flags |= FILE_FLAG_OPEN_REPARSE_POINT ;
@@ -1972,8 +2157,12 @@ INLINE static DWORD fs__stat_impl_from_path(WCHAR* path,
1972
2157
flags ,
1973
2158
NULL );
1974
2159
1975
- if (handle == INVALID_HANDLE_VALUE )
1976
- return GetLastError ();
2160
+ if (handle == INVALID_HANDLE_VALUE ) {
2161
+ ret = GetLastError ();
2162
+ if (ret != ERROR_ACCESS_DENIED && ret != ERROR_SHARING_VIOLATION )
2163
+ return ret ;
2164
+ return fs__stat_directory (path , statbuf , do_lstat , ret );
2165
+ }
1977
2166
1978
2167
if (fs__stat_handle (handle , statbuf , do_lstat ) != 0 )
1979
2168
ret = GetLastError ();
0 commit comments