11#include "common/path.h"
22#include "common/io.h"
3- #include "common/stringUtils .h"
3+ #include "common/arrayUtils .h"
44
55#if !_WIN32
66const char * ffFindExecutableInPath (const char * name , FFstrbuf * result )
@@ -49,6 +49,9 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
4949}
5050#else
5151#include <windows.h>
52+ #include <winioctl.h>
53+ #include <errno.h>
54+ #include <stdalign.h>
5255
5356const char * ffFindExecutableInPath (const char * name , FFstrbuf * result )
5457{
@@ -62,4 +65,260 @@ const char* ffFindExecutableInPath(const char* name, FFstrbuf* result)
6265 ffStrbufSetS (result , buffer );
6366 return NULL ;
6467}
68+
69+ static inline int winerr2Errno (DWORD err )
70+ {
71+ switch (err )
72+ {
73+ case ERROR_FILE_NOT_FOUND :
74+ case ERROR_PATH_NOT_FOUND :
75+ case ERROR_INVALID_NAME :
76+ return ENOENT ;
77+ case ERROR_ACCESS_DENIED :
78+ case ERROR_SHARING_VIOLATION :
79+ case ERROR_LOCK_VIOLATION :
80+ return EACCES ;
81+ case ERROR_BUFFER_OVERFLOW :
82+ case ERROR_INSUFFICIENT_BUFFER :
83+ return ENAMETOOLONG ;
84+ case ERROR_INVALID_PARAMETER :
85+ case ERROR_NOT_A_REPARSE_POINT :
86+ return EINVAL ;
87+ default :
88+ return EIO ;
89+ }
90+ }
91+
92+ char * frealpath (HANDLE hFile , char * resolved_name )
93+ {
94+ if (__builtin_expect (hFile == INVALID_HANDLE_VALUE || !hFile , false))
95+ {
96+ errno = EINVAL ;
97+ return NULL ;
98+ }
99+
100+ wchar_t resolvedNameW [MAX_PATH + 4 ]; /* +4 for "\\\\?\\" prefix */
101+ DWORD lenW = GetFinalPathNameByHandleW (hFile , resolvedNameW , (DWORD )ARRAY_SIZE (resolvedNameW ), FILE_NAME_NORMALIZED );
102+
103+ if (lenW == 0 )
104+ {
105+ errno = winerr2Errno (GetLastError ());
106+ return NULL ;
107+ }
108+ if (lenW >= ARRAY_SIZE (resolvedNameW ))
109+ {
110+ errno = E2BIG ;
111+ return NULL ;
112+ }
113+ lenW ++ ; // Include null terminator
114+
115+ wchar_t * srcW = resolvedNameW ;
116+ DWORD srcLenW = lenW ;
117+
118+ if (srcLenW >= 8 && wcsncmp (resolvedNameW , L"\\\\?\\UNC\\" , 8 ) == 0 )
119+ {
120+ /* Convert "\\?\UNC\server\share" to "\\server\share" */
121+ srcW += 6 ;
122+ srcLenW -= 6 ;
123+ * srcW = L'\\' ;
124+ }
125+ else if (srcLenW >= 4 && wcsncmp (resolvedNameW , L"\\\\?\\" , 4 ) == 0 )
126+ {
127+ srcW += 4 ;
128+ srcLenW -= 4 ;
129+ }
130+
131+ if (resolved_name )
132+ {
133+ ULONG outBytes = 0 ;
134+ if (!NT_SUCCESS (RtlUnicodeToUTF8N (resolved_name , MAX_PATH , & outBytes , srcW , (ULONG )(srcLenW * sizeof (wchar_t )))))
135+ {
136+ errno = E2BIG ;
137+ return NULL ;
138+ }
139+
140+ if (outBytes > MAX_PATH )
141+ {
142+ errno = E2BIG ;
143+ return NULL ;
144+ }
145+
146+ return resolved_name ;
147+ }
148+ else
149+ {
150+ /* UTF-8 worst-case: up to 4 bytes per UTF-16 code unit */
151+ char tmp [(MAX_PATH + 4 ) * 4 ];
152+ ULONG outBytes = 0 ;
153+
154+ if (!NT_SUCCESS (RtlUnicodeToUTF8N (tmp , (ULONG )sizeof (tmp ), & outBytes , srcW , (ULONG )(srcLenW * sizeof (wchar_t )))))
155+ {
156+ errno = E2BIG ;
157+ return NULL ;
158+ }
159+
160+ resolved_name = (char * )malloc (outBytes );
161+ if (!resolved_name )
162+ {
163+ errno = ENOMEM ;
164+ return NULL ;
165+ }
166+
167+ memcpy (resolved_name , tmp , outBytes );
168+ return resolved_name ;
169+ }
170+
171+ return resolved_name ;
172+ }
173+
174+ char * realpath (const char * __restrict file_name , char * __restrict resolved_name )
175+ {
176+ if (!file_name )
177+ {
178+ errno = EINVAL ;
179+ return NULL ;
180+ }
181+
182+ wchar_t fileNameW [MAX_PATH ];
183+ ULONG lenBytes = 0 ;
184+
185+ if (!NT_SUCCESS (RtlUTF8ToUnicodeN (fileNameW , (ULONG )sizeof (fileNameW ), & lenBytes , file_name , (ULONG )strlen (file_name ) + 1 )))
186+ {
187+ errno = EINVAL ;
188+ return NULL ;
189+ }
190+
191+ FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW (
192+ fileNameW ,
193+ 0 ,
194+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
195+ NULL ,
196+ OPEN_EXISTING ,
197+ FILE_FLAG_BACKUP_SEMANTICS ,
198+ NULL );
199+
200+ if (hFile == INVALID_HANDLE_VALUE )
201+ {
202+ errno = winerr2Errno (GetLastError ());
203+ return NULL ;
204+ }
205+
206+ return frealpath (hFile , resolved_name );
207+ }
208+
209+ ssize_t freadlink (HANDLE hFile , char * buf , size_t bufsiz )
210+ {
211+ if (__builtin_expect (hFile == INVALID_HANDLE_VALUE || !buf || bufsiz == 0 , false))
212+ {
213+ errno = EINVAL ;
214+ return -1 ;
215+ }
216+
217+ alignas(REPARSE_DATA_BUFFER ) BYTE reparseBuf [MAXIMUM_REPARSE_DATA_BUFFER_SIZE ];
218+ DWORD bytesReturned = 0 ;
219+ if (!DeviceIoControl (hFile , FSCTL_GET_REPARSE_POINT , NULL , 0 , reparseBuf , (DWORD ) sizeof (reparseBuf ), & bytesReturned , NULL ))
220+ {
221+ errno = winerr2Errno (GetLastError ());
222+ return -1 ;
223+ }
224+
225+ REPARSE_DATA_BUFFER * rp = (REPARSE_DATA_BUFFER * ) reparseBuf ;
226+ const wchar_t * targetW = NULL ;
227+ USHORT targetBytes = 0 ;
228+
229+ if (rp -> ReparseTag == IO_REPARSE_TAG_SYMLINK )
230+ {
231+ if (rp -> SymbolicLinkReparseBuffer .PrintNameLength > 0 )
232+ {
233+ targetW = rp -> SymbolicLinkReparseBuffer .PathBuffer +
234+ (rp -> SymbolicLinkReparseBuffer .PrintNameOffset / sizeof (wchar_t ));
235+ targetBytes = rp -> SymbolicLinkReparseBuffer .PrintNameLength ;
236+ }
237+ else
238+ {
239+ targetW = rp -> SymbolicLinkReparseBuffer .PathBuffer +
240+ (rp -> SymbolicLinkReparseBuffer .SubstituteNameOffset / sizeof (wchar_t ));
241+ targetBytes = rp -> SymbolicLinkReparseBuffer .SubstituteNameLength ;
242+
243+ if (targetBytes >= 8 &&
244+ wcsncmp (targetW , L"\\??\\" , 4 ) == 0 )
245+ {
246+ targetW += 4 ;
247+ targetBytes -= 8 ;
248+ }
249+ }
250+ }
251+ else if (rp -> ReparseTag == IO_REPARSE_TAG_MOUNT_POINT )
252+ {
253+ if (rp -> MountPointReparseBuffer .PrintNameLength > 0 )
254+ {
255+ targetW = rp -> MountPointReparseBuffer .PathBuffer +
256+ (rp -> MountPointReparseBuffer .PrintNameOffset / sizeof (wchar_t ));
257+ targetBytes = rp -> MountPointReparseBuffer .PrintNameLength ;
258+ }
259+ else
260+ {
261+ targetW = rp -> MountPointReparseBuffer .PathBuffer +
262+ (rp -> MountPointReparseBuffer .SubstituteNameOffset / sizeof (wchar_t ));
263+ targetBytes = rp -> MountPointReparseBuffer .SubstituteNameLength ;
264+
265+ if (targetBytes >= 8 &&
266+ wcsncmp (targetW , L"\\??\\" , 4 ) == 0 )
267+ {
268+ targetW += 4 ;
269+ targetBytes -= 8 ;
270+ }
271+ }
272+ }
273+ else
274+ {
275+ errno = EINVAL ;
276+ return -1 ;
277+ }
278+
279+ ULONG outBytes = 0 ;
280+ if (!NT_SUCCESS (RtlUnicodeToUTF8N (buf , (ULONG ) bufsiz , & outBytes , targetW , targetBytes )))
281+ {
282+ errno = E2BIG ;
283+ return -1 ;
284+ }
285+
286+ // Not null-terminated
287+ return (ssize_t ) outBytes ;
288+ }
289+
290+ ssize_t readlink (const char * path , char * buf , size_t bufsiz )
291+ {
292+ if (!path || !buf || bufsiz == 0 )
293+ {
294+ errno = EINVAL ;
295+ return -1 ;
296+ }
297+
298+ wchar_t pathW [MAX_PATH ];
299+ ULONG pathWBytes = 0 ;
300+ if (!NT_SUCCESS (RtlUTF8ToUnicodeN (pathW , (ULONG ) sizeof (pathW ), & pathWBytes , path , (ULONG ) strlen (path ) + 1 )))
301+ {
302+ errno = EINVAL ;
303+ return -1 ;
304+ }
305+
306+ FF_AUTO_CLOSE_FD HANDLE hFile = CreateFileW (
307+ pathW ,
308+ 0 ,
309+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ,
310+ NULL ,
311+ OPEN_EXISTING ,
312+ FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT ,
313+ NULL
314+ );
315+
316+ if (hFile == INVALID_HANDLE_VALUE )
317+ {
318+ errno = winerr2Errno (GetLastError ());
319+ return -1 ;
320+ }
321+
322+ return freadlink (hFile , buf , bufsiz );
323+ }
65324#endif
0 commit comments