1 /**
2  * WinPR: Windows Portable Runtime
3  * File Functions
4  *
5  * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6  * Copyright 2014 Hewlett-Packard Development Company, L.P.
7  * Copyright 2016 David PHAM-VAN <d.phamvan@inuvika.com>
8  *
9  * Licensed under the Apache License, Version 2.0 (the "License");
10  * you may not use this file except in compliance with the License.
11  * You may obtain a copy of the License at
12  *
13  *     http://www.apache.org/licenses/LICENSE-2.0
14  *
15  * Unless required by applicable law or agreed to in writing, software
16  * distributed under the License is distributed on an "AS IS" BASIS,
17  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18  * See the License for the specific language governing permissions and
19  * limitations under the License.
20  */
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <winpr/crt.h>
27 #include <winpr/path.h>
28 #include <winpr/file.h>
29 
30 #ifdef HAVE_UNISTD_H
31 #include <unistd.h>
32 #endif
33 
34 #ifdef HAVE_FCNTL_H
35 #include <fcntl.h>
36 #endif
37 
38 #include "../log.h"
39 #define TAG WINPR_TAG("file")
40 
41 #ifdef _WIN32
42 #include <io.h>
43 #include <sys/stat.h>
44 #else
45 #include <assert.h>
46 #include <pthread.h>
47 #include <dirent.h>
48 #include <libgen.h>
49 #include <errno.h>
50 
51 #include <sys/un.h>
52 #include <sys/stat.h>
53 #include <sys/socket.h>
54 
55 #ifdef HAVE_AIO_H
56 #undef HAVE_AIO_H /* disable for now, incomplete */
57 #endif
58 
59 #ifdef HAVE_AIO_H
60 #include <aio.h>
61 #endif
62 
63 #ifdef ANDROID
64 #include <sys/vfs.h>
65 #else
66 #include <sys/statvfs.h>
67 #endif
68 
69 #include "../handle/handle.h"
70 
71 #include "../pipe/pipe.h"
72 
73 #include "file.h"
74 
75 /**
76  * api-ms-win-core-file-l1-2-0.dll:
77  *
78  * CreateFileA
79  * CreateFileW
80  * CreateFile2
81  * DeleteFileA
82  * DeleteFileW
83  * CreateDirectoryA
84  * CreateDirectoryW
85  * RemoveDirectoryA
86  * RemoveDirectoryW
87  * CompareFileTime
88  * DefineDosDeviceW
89  * DeleteVolumeMountPointW
90  * FileTimeToLocalFileTime
91  * LocalFileTimeToFileTime
92  * FindClose
93  * FindCloseChangeNotification
94  * FindFirstChangeNotificationA
95  * FindFirstChangeNotificationW
96  * FindFirstFileA
97  * FindFirstFileExA
98  * FindFirstFileExW
99  * FindFirstFileW
100  * FindFirstVolumeW
101  * FindNextChangeNotification
102  * FindNextFileA
103  * FindNextFileW
104  * FindNextVolumeW
105  * FindVolumeClose
106  * GetDiskFreeSpaceA
107  * GetDiskFreeSpaceExA
108  * GetDiskFreeSpaceExW
109  * GetDiskFreeSpaceW
110  * GetDriveTypeA
111  * GetDriveTypeW
112  * GetFileAttributesA
113  * GetFileAttributesExA
114  * GetFileAttributesExW
115  * GetFileAttributesW
116  * GetFileInformationByHandle
117  * GetFileSize
118  * GetFileSizeEx
119  * GetFileTime
120  * GetFileType
121  * GetFinalPathNameByHandleA
122  * GetFinalPathNameByHandleW
123  * GetFullPathNameA
124  * GetFullPathNameW
125  * GetLogicalDrives
126  * GetLogicalDriveStringsW
127  * GetLongPathNameA
128  * GetLongPathNameW
129  * GetShortPathNameW
130  * GetTempFileNameW
131  * GetTempPathW
132  * GetVolumeInformationByHandleW
133  * GetVolumeInformationW
134  * GetVolumeNameForVolumeMountPointW
135  * GetVolumePathNamesForVolumeNameW
136  * GetVolumePathNameW
137  * QueryDosDeviceW
138  * SetFileAttributesA
139  * SetFileAttributesW
140  * SetFileTime
141  * SetFileValidData
142  * SetFileInformationByHandle
143  * ReadFile
144  * ReadFileEx
145  * ReadFileScatter
146  * WriteFile
147  * WriteFileEx
148  * WriteFileGather
149  * FlushFileBuffers
150  * SetEndOfFile
151  * SetFilePointer
152  * SetFilePointerEx
153  * LockFile
154  * LockFileEx
155  * UnlockFile
156  * UnlockFileEx
157  */
158 
159 /**
160  * File System Behavior in the Microsoft Windows Environment:
161  * http://download.microsoft.com/download/4/3/8/43889780-8d45-4b2e-9d3a-c696a890309f/File%20System%20Behavior%20Overview.pdf
162  */
163 
164 /**
165  * Asynchronous I/O - The GNU C Library:
166  * http://www.gnu.org/software/libc/manual/html_node/Asynchronous-I_002fO.html
167  */
168 
169 /**
170  * aio.h - asynchronous input and output:
171  * http://pubs.opengroup.org/onlinepubs/009695399/basedefs/aio.h.html
172  */
173 
174 /**
175  * Asynchronous I/O User Guide:
176  * http://code.google.com/p/kernel/wiki/AIOUserGuide
177  */
178 
179 #define EPOCH_DIFF 11644473600LL
180 #define STAT_TIME_TO_FILETIME(_t) (((UINT64)(_t) + EPOCH_DIFF) * 10000000LL)
181 
182 static wArrayList* _HandleCreators;
183 
184 static pthread_once_t _HandleCreatorsInitialized = PTHREAD_ONCE_INIT;
185 
186 extern HANDLE_CREATOR* GetNamedPipeClientHandleCreator(void);
187 
188 #if defined __linux__ && !defined ANDROID
189 extern HANDLE_CREATOR* GetCommHandleCreator(void);
190 #endif /* __linux__ && !defined ANDROID */
191 
_HandleCreatorsInit()192 static void _HandleCreatorsInit()
193 {
194 	assert(_HandleCreators == NULL);
195 	_HandleCreators = ArrayList_New(TRUE);
196 
197 	if (!_HandleCreators)
198 		return;
199 
200 	/*
201 	 * Register all file handle creators.
202 	 */
203 	ArrayList_Add(_HandleCreators, GetNamedPipeClientHandleCreator());
204 #if defined __linux__ && !defined ANDROID
205 	ArrayList_Add(_HandleCreators, GetCommHandleCreator());
206 #endif /* __linux__ && !defined ANDROID */
207 	ArrayList_Add(_HandleCreators, GetFileHandleCreator());
208 }
209 
210 #ifdef HAVE_AIO_H
211 
212 static BOOL g_AioSignalHandlerInstalled = FALSE;
213 
AioSignalHandler(int signum,siginfo_t * siginfo,void * arg)214 void AioSignalHandler(int signum, siginfo_t* siginfo, void* arg)
215 {
216 	WLog_INFO("%d", signum);
217 }
218 
InstallAioSignalHandler()219 int InstallAioSignalHandler()
220 {
221 	if (!g_AioSignalHandlerInstalled)
222 	{
223 		struct sigaction action;
224 		sigemptyset(&action.sa_mask);
225 		sigaddset(&action.sa_mask, SIGIO);
226 		action.sa_flags = SA_SIGINFO;
227 		action.sa_sigaction = (void*)&AioSignalHandler;
228 		sigaction(SIGIO, &action, NULL);
229 		g_AioSignalHandlerInstalled = TRUE;
230 	}
231 
232 	return 0;
233 }
234 
235 #endif /* HAVE_AIO_H */
236 
CreateFileA(LPCSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)237 HANDLE CreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
238                    LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
239                    DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
240 {
241 	int i;
242 
243 	if (!lpFileName)
244 		return INVALID_HANDLE_VALUE;
245 
246 	if (pthread_once(&_HandleCreatorsInitialized, _HandleCreatorsInit) != 0)
247 	{
248 		SetLastError(ERROR_DLL_INIT_FAILED);
249 		return INVALID_HANDLE_VALUE;
250 	}
251 
252 	if (_HandleCreators == NULL)
253 	{
254 		SetLastError(ERROR_DLL_INIT_FAILED);
255 		return INVALID_HANDLE_VALUE;
256 	}
257 
258 	ArrayList_Lock(_HandleCreators);
259 
260 	for (i = 0; i <= ArrayList_Count(_HandleCreators); i++)
261 	{
262 		HANDLE_CREATOR* creator = ArrayList_GetItem(_HandleCreators, i);
263 
264 		if (creator && creator->IsHandled(lpFileName))
265 		{
266 			HANDLE newHandle =
267 			    creator->CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
268 			                         dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
269 			ArrayList_Unlock(_HandleCreators);
270 			return newHandle;
271 		}
272 	}
273 
274 	ArrayList_Unlock(_HandleCreators);
275 	return INVALID_HANDLE_VALUE;
276 }
277 
CreateFileW(LPCWSTR lpFileName,DWORD dwDesiredAccess,DWORD dwShareMode,LPSECURITY_ATTRIBUTES lpSecurityAttributes,DWORD dwCreationDisposition,DWORD dwFlagsAndAttributes,HANDLE hTemplateFile)278 HANDLE CreateFileW(LPCWSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode,
279                    LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
280                    DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
281 {
282 	LPSTR lpFileNameA = NULL;
283 	HANDLE hdl;
284 
285 	if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpFileNameA, 0, NULL, NULL) < 1)
286 		return NULL;
287 
288 	hdl = CreateFileA(lpFileNameA, dwDesiredAccess, dwShareMode, lpSecurityAttributes,
289 	                  dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
290 	free(lpFileNameA);
291 	return hdl;
292 }
293 
DeleteFileA(LPCSTR lpFileName)294 BOOL DeleteFileA(LPCSTR lpFileName)
295 {
296 	int status;
297 	status = unlink(lpFileName);
298 	return (status != -1) ? TRUE : FALSE;
299 }
300 
DeleteFileW(LPCWSTR lpFileName)301 BOOL DeleteFileW(LPCWSTR lpFileName)
302 {
303 	LPSTR lpFileNameA = NULL;
304 	BOOL rc = FALSE;
305 
306 	if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpFileNameA, 0, NULL, NULL) < 1)
307 		return FALSE;
308 
309 	rc = DeleteFileA(lpFileNameA);
310 	free(lpFileNameA);
311 	return rc;
312 }
313 
ReadFile(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,LPDWORD lpNumberOfBytesRead,LPOVERLAPPED lpOverlapped)314 BOOL ReadFile(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
315               LPDWORD lpNumberOfBytesRead, LPOVERLAPPED lpOverlapped)
316 {
317 	ULONG Type;
318 	WINPR_HANDLE* handle;
319 
320 	if (hFile == INVALID_HANDLE_VALUE)
321 		return FALSE;
322 
323 	/*
324 	 * from http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467%28v=vs.85%29.aspx
325 	 * lpNumberOfBytesRead can be NULL only when the lpOverlapped parameter is not NULL.
326 	 */
327 
328 	if (!lpNumberOfBytesRead && !lpOverlapped)
329 		return FALSE;
330 
331 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
332 		return FALSE;
333 
334 	handle = (WINPR_HANDLE*)hFile;
335 
336 	if (handle->ops->ReadFile)
337 		return handle->ops->ReadFile(handle, lpBuffer, nNumberOfBytesToRead, lpNumberOfBytesRead,
338 		                             lpOverlapped);
339 
340 	WLog_ERR(TAG, "ReadFile operation not implemented");
341 	return FALSE;
342 }
343 
ReadFileEx(HANDLE hFile,LPVOID lpBuffer,DWORD nNumberOfBytesToRead,LPOVERLAPPED lpOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)344 BOOL ReadFileEx(HANDLE hFile, LPVOID lpBuffer, DWORD nNumberOfBytesToRead,
345                 LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
346 {
347 	ULONG Type;
348 	WINPR_HANDLE* handle;
349 
350 	if (hFile == INVALID_HANDLE_VALUE)
351 		return FALSE;
352 
353 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
354 		return FALSE;
355 
356 	handle = (WINPR_HANDLE*)hFile;
357 
358 	if (handle->ops->ReadFileEx)
359 		return handle->ops->ReadFileEx(handle, lpBuffer, nNumberOfBytesToRead, lpOverlapped,
360 		                               lpCompletionRoutine);
361 
362 	WLog_ERR(TAG, "ReadFileEx operation not implemented");
363 	return FALSE;
364 }
365 
ReadFileScatter(HANDLE hFile,FILE_SEGMENT_ELEMENT aSegmentArray[],DWORD nNumberOfBytesToRead,LPDWORD lpReserved,LPOVERLAPPED lpOverlapped)366 BOOL ReadFileScatter(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[], DWORD nNumberOfBytesToRead,
367                      LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
368 {
369 	ULONG Type;
370 	WINPR_HANDLE* handle;
371 
372 	if (hFile == INVALID_HANDLE_VALUE)
373 		return FALSE;
374 
375 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
376 		return FALSE;
377 
378 	handle = (WINPR_HANDLE*)hFile;
379 
380 	if (handle->ops->ReadFileScatter)
381 		return handle->ops->ReadFileScatter(handle, aSegmentArray, nNumberOfBytesToRead, lpReserved,
382 		                                    lpOverlapped);
383 
384 	WLog_ERR(TAG, "ReadFileScatter operation not implemented");
385 	return FALSE;
386 }
387 
WriteFile(HANDLE hFile,LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite,LPDWORD lpNumberOfBytesWritten,LPOVERLAPPED lpOverlapped)388 BOOL WriteFile(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
389                LPDWORD lpNumberOfBytesWritten, LPOVERLAPPED lpOverlapped)
390 {
391 	ULONG Type;
392 	WINPR_HANDLE* handle;
393 
394 	if (hFile == INVALID_HANDLE_VALUE)
395 		return FALSE;
396 
397 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
398 		return FALSE;
399 
400 	handle = (WINPR_HANDLE*)hFile;
401 
402 	if (handle->ops->WriteFile)
403 		return handle->ops->WriteFile(handle, lpBuffer, nNumberOfBytesToWrite,
404 		                              lpNumberOfBytesWritten, lpOverlapped);
405 
406 	WLog_ERR(TAG, "WriteFile operation not implemented");
407 	return FALSE;
408 }
409 
WriteFileEx(HANDLE hFile,LPCVOID lpBuffer,DWORD nNumberOfBytesToWrite,LPOVERLAPPED lpOverlapped,LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)410 BOOL WriteFileEx(HANDLE hFile, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrite,
411                  LPOVERLAPPED lpOverlapped, LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine)
412 {
413 	ULONG Type;
414 	WINPR_HANDLE* handle;
415 
416 	if (hFile == INVALID_HANDLE_VALUE)
417 		return FALSE;
418 
419 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
420 		return FALSE;
421 
422 	handle = (WINPR_HANDLE*)hFile;
423 
424 	if (handle->ops->WriteFileEx)
425 		return handle->ops->WriteFileEx(handle, lpBuffer, nNumberOfBytesToWrite, lpOverlapped,
426 		                                lpCompletionRoutine);
427 
428 	WLog_ERR(TAG, "WriteFileEx operation not implemented");
429 	return FALSE;
430 }
431 
WriteFileGather(HANDLE hFile,FILE_SEGMENT_ELEMENT aSegmentArray[],DWORD nNumberOfBytesToWrite,LPDWORD lpReserved,LPOVERLAPPED lpOverlapped)432 BOOL WriteFileGather(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[],
433                      DWORD nNumberOfBytesToWrite, LPDWORD lpReserved, LPOVERLAPPED lpOverlapped)
434 {
435 	ULONG Type;
436 	WINPR_HANDLE* handle;
437 
438 	if (hFile == INVALID_HANDLE_VALUE)
439 		return FALSE;
440 
441 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
442 		return FALSE;
443 
444 	handle = (WINPR_HANDLE*)hFile;
445 
446 	if (handle->ops->WriteFileGather)
447 		return handle->ops->WriteFileGather(handle, aSegmentArray, nNumberOfBytesToWrite,
448 		                                    lpReserved, lpOverlapped);
449 
450 	WLog_ERR(TAG, "WriteFileGather operation not implemented");
451 	return FALSE;
452 }
453 
FlushFileBuffers(HANDLE hFile)454 BOOL FlushFileBuffers(HANDLE hFile)
455 {
456 	ULONG Type;
457 	WINPR_HANDLE* handle;
458 
459 	if (hFile == INVALID_HANDLE_VALUE)
460 		return FALSE;
461 
462 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
463 		return FALSE;
464 
465 	handle = (WINPR_HANDLE*)hFile;
466 
467 	if (handle->ops->FlushFileBuffers)
468 		return handle->ops->FlushFileBuffers(handle);
469 
470 	WLog_ERR(TAG, "FlushFileBuffers operation not implemented");
471 	return FALSE;
472 }
473 
GetFileAttributesExA(LPCSTR lpFileName,GET_FILEEX_INFO_LEVELS fInfoLevelId,LPVOID lpFileInformation)474 BOOL WINAPI GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
475                                  LPVOID lpFileInformation)
476 {
477 	LPWIN32_FILE_ATTRIBUTE_DATA fd = lpFileInformation;
478 	WIN32_FIND_DATAA findFileData;
479 	HANDLE hFind;
480 
481 	if (!fd)
482 		return FALSE;
483 
484 	if ((hFind = FindFirstFileA(lpFileName, &findFileData)) == INVALID_HANDLE_VALUE)
485 		return FALSE;
486 
487 	FindClose(hFind);
488 	fd->dwFileAttributes = findFileData.dwFileAttributes;
489 	fd->ftCreationTime = findFileData.ftCreationTime;
490 	fd->ftLastAccessTime = findFileData.ftLastAccessTime;
491 	fd->ftLastWriteTime = findFileData.ftLastWriteTime;
492 	fd->nFileSizeHigh = findFileData.nFileSizeHigh;
493 	fd->nFileSizeLow = findFileData.nFileSizeLow;
494 	return TRUE;
495 }
496 
GetFileAttributesExW(LPCWSTR lpFileName,GET_FILEEX_INFO_LEVELS fInfoLevelId,LPVOID lpFileInformation)497 BOOL WINAPI GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId,
498                                  LPVOID lpFileInformation)
499 {
500 	BOOL ret;
501 	LPSTR lpCFileName;
502 
503 	if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0)
504 	{
505 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
506 		return FALSE;
507 	}
508 
509 	ret = GetFileAttributesExA(lpCFileName, fInfoLevelId, lpFileInformation);
510 	free(lpCFileName);
511 	return ret;
512 }
513 
GetFileAttributesA(LPCSTR lpFileName)514 DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName)
515 {
516 	WIN32_FIND_DATAA findFileData;
517 	HANDLE hFind;
518 
519 	if ((hFind = FindFirstFileA(lpFileName, &findFileData)) == INVALID_HANDLE_VALUE)
520 		return INVALID_FILE_ATTRIBUTES;
521 
522 	FindClose(hFind);
523 	return findFileData.dwFileAttributes;
524 }
525 
GetFileAttributesW(LPCWSTR lpFileName)526 DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName)
527 {
528 	DWORD ret;
529 	LPSTR lpCFileName;
530 
531 	if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0)
532 	{
533 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
534 		return FALSE;
535 	}
536 
537 	ret = GetFileAttributesA(lpCFileName);
538 	free(lpCFileName);
539 	return ret;
540 }
541 
append(char * buffer,size_t size,const char * append)542 static char* append(char* buffer, size_t size, const char* append)
543 {
544 	const size_t len = strnlen(buffer, size);
545 	if (len == 0)
546 		_snprintf(buffer, size, "%s", append);
547 	else
548 	{
549 		strcat(buffer, "|");
550 		strcat(buffer, append);
551 	}
552 
553 	return buffer;
554 }
555 
flagsToStr(char * buffer,size_t size,DWORD flags)556 static const char* flagsToStr(char* buffer, size_t size, DWORD flags)
557 {
558 	char strflags[32] = { 0 };
559 	if (flags & FILE_ATTRIBUTE_READONLY)
560 		append(buffer, size, "FILE_ATTRIBUTE_READONLY");
561 	if (flags & FILE_ATTRIBUTE_HIDDEN)
562 		append(buffer, size, "FILE_ATTRIBUTE_HIDDEN");
563 	if (flags & FILE_ATTRIBUTE_SYSTEM)
564 		append(buffer, size, "FILE_ATTRIBUTE_SYSTEM");
565 	if (flags & FILE_ATTRIBUTE_DIRECTORY)
566 		append(buffer, size, "FILE_ATTRIBUTE_DIRECTORY");
567 	if (flags & FILE_ATTRIBUTE_ARCHIVE)
568 		append(buffer, size, "FILE_ATTRIBUTE_ARCHIVE");
569 	if (flags & FILE_ATTRIBUTE_DEVICE)
570 		append(buffer, size, "FILE_ATTRIBUTE_DEVICE");
571 	if (flags & FILE_ATTRIBUTE_NORMAL)
572 		append(buffer, size, "FILE_ATTRIBUTE_NORMAL");
573 	if (flags & FILE_ATTRIBUTE_TEMPORARY)
574 		append(buffer, size, "FILE_ATTRIBUTE_TEMPORARY");
575 	if (flags & FILE_ATTRIBUTE_SPARSE_FILE)
576 		append(buffer, size, "FILE_ATTRIBUTE_SPARSE_FILE");
577 	if (flags & FILE_ATTRIBUTE_REPARSE_POINT)
578 		append(buffer, size, "FILE_ATTRIBUTE_REPARSE_POINT");
579 	if (flags & FILE_ATTRIBUTE_COMPRESSED)
580 		append(buffer, size, "FILE_ATTRIBUTE_COMPRESSED");
581 	if (flags & FILE_ATTRIBUTE_OFFLINE)
582 		append(buffer, size, "FILE_ATTRIBUTE_OFFLINE");
583 	if (flags & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED)
584 		append(buffer, size, "FILE_ATTRIBUTE_NOT_CONTENT_INDEXED");
585 	if (flags & FILE_ATTRIBUTE_ENCRYPTED)
586 		append(buffer, size, "FILE_ATTRIBUTE_ENCRYPTED");
587 	if (flags & FILE_ATTRIBUTE_VIRTUAL)
588 		append(buffer, size, "FILE_ATTRIBUTE_VIRTUAL");
589 
590 	_snprintf(strflags, sizeof(strflags), " [0x%08" PRIx32 "]", flags);
591 	strcat(buffer, strflags);
592 	return buffer;
593 }
594 
SetFileAttributesA(LPCSTR lpFileName,DWORD dwFileAttributes)595 BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes)
596 {
597 	struct stat st;
598 	int fd;
599 	BOOL rc = FALSE;
600 
601 	if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
602 	{
603 		char buffer[8192] = { 0 };
604 		const char* flags =
605 		    flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
606 		WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags);
607 	}
608 
609 	fd = open(lpFileName, O_RDONLY);
610 	if (fd < 0)
611 		return FALSE;
612 
613 	if (fstat(fd, &st) != 0)
614 		goto fail;
615 
616 	if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
617 	{
618 		st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
619 	}
620 	else
621 	{
622 		st.st_mode |= S_IWUSR;
623 	}
624 
625 	if (fchmod(fd, st.st_mode) != 0)
626 		goto fail;
627 
628 	rc = TRUE;
629 fail:
630 	close(fd);
631 	return rc;
632 }
633 
SetFileAttributesW(LPCWSTR lpFileName,DWORD dwFileAttributes)634 BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes)
635 {
636 	BOOL ret;
637 	LPSTR lpCFileName;
638 
639 	if (dwFileAttributes & ~FILE_ATTRIBUTE_READONLY)
640 	{
641 		char buffer[8192] = { 0 };
642 		const char* flags =
643 		    flagsToStr(buffer, sizeof(buffer), dwFileAttributes & ~FILE_ATTRIBUTE_READONLY);
644 		WLog_WARN(TAG, "[%s] Unsupported flags %s, ignoring!", __FUNCTION__, flags);
645 	}
646 
647 	if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0)
648 	{
649 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
650 		return FALSE;
651 	}
652 
653 	ret = SetFileAttributesA(lpCFileName, dwFileAttributes);
654 	free(lpCFileName);
655 	return ret;
656 }
657 
SetEndOfFile(HANDLE hFile)658 BOOL SetEndOfFile(HANDLE hFile)
659 {
660 	ULONG Type;
661 	WINPR_HANDLE* handle;
662 
663 	if (hFile == INVALID_HANDLE_VALUE)
664 		return FALSE;
665 
666 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
667 		return FALSE;
668 
669 	handle = (WINPR_HANDLE*)hFile;
670 
671 	if (handle->ops->SetEndOfFile)
672 		return handle->ops->SetEndOfFile(handle);
673 
674 	WLog_ERR(TAG, "SetEndOfFile operation not implemented");
675 	return FALSE;
676 }
677 
GetFileSize(HANDLE hFile,LPDWORD lpFileSizeHigh)678 DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh)
679 {
680 	ULONG Type;
681 	WINPR_HANDLE* handle;
682 
683 	if (hFile == INVALID_HANDLE_VALUE)
684 		return FALSE;
685 
686 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
687 		return FALSE;
688 
689 	handle = (WINPR_HANDLE*)hFile;
690 
691 	if (handle->ops->GetFileSize)
692 		return handle->ops->GetFileSize(handle, lpFileSizeHigh);
693 
694 	WLog_ERR(TAG, "GetFileSize operation not implemented");
695 	return 0;
696 }
697 
SetFilePointer(HANDLE hFile,LONG lDistanceToMove,PLONG lpDistanceToMoveHigh,DWORD dwMoveMethod)698 DWORD SetFilePointer(HANDLE hFile, LONG lDistanceToMove, PLONG lpDistanceToMoveHigh,
699                      DWORD dwMoveMethod)
700 {
701 	ULONG Type;
702 	WINPR_HANDLE* handle;
703 
704 	if (hFile == INVALID_HANDLE_VALUE)
705 		return FALSE;
706 
707 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
708 		return FALSE;
709 
710 	handle = (WINPR_HANDLE*)hFile;
711 
712 	if (handle->ops->SetFilePointer)
713 		return handle->ops->SetFilePointer(handle, lDistanceToMove, lpDistanceToMoveHigh,
714 		                                   dwMoveMethod);
715 
716 	WLog_ERR(TAG, "SetFilePointer operation not implemented");
717 	return 0;
718 }
719 
SetFilePointerEx(HANDLE hFile,LARGE_INTEGER liDistanceToMove,PLARGE_INTEGER lpNewFilePointer,DWORD dwMoveMethod)720 BOOL SetFilePointerEx(HANDLE hFile, LARGE_INTEGER liDistanceToMove, PLARGE_INTEGER lpNewFilePointer,
721                       DWORD dwMoveMethod)
722 {
723 	ULONG Type;
724 	WINPR_HANDLE* handle;
725 
726 	if (hFile == INVALID_HANDLE_VALUE)
727 		return FALSE;
728 
729 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
730 		return FALSE;
731 
732 	handle = (WINPR_HANDLE*)hFile;
733 
734 	if (handle->ops->SetFilePointerEx)
735 		return handle->ops->SetFilePointerEx(handle, liDistanceToMove, lpNewFilePointer,
736 		                                     dwMoveMethod);
737 
738 	WLog_ERR(TAG, "SetFilePointerEx operation not implemented");
739 	return 0;
740 }
741 
LockFile(HANDLE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh)742 BOOL LockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
743               DWORD nNumberOfBytesToLockLow, DWORD nNumberOfBytesToLockHigh)
744 {
745 	ULONG Type;
746 	WINPR_HANDLE* handle;
747 
748 	if (hFile == INVALID_HANDLE_VALUE)
749 		return FALSE;
750 
751 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
752 		return FALSE;
753 
754 	handle = (WINPR_HANDLE*)hFile;
755 
756 	if (handle->ops->LockFile)
757 		return handle->ops->LockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
758 		                             nNumberOfBytesToLockLow, nNumberOfBytesToLockHigh);
759 
760 	WLog_ERR(TAG, "LockFile operation not implemented");
761 	return FALSE;
762 }
763 
LockFileEx(HANDLE hFile,DWORD dwFlags,DWORD dwReserved,DWORD nNumberOfBytesToLockLow,DWORD nNumberOfBytesToLockHigh,LPOVERLAPPED lpOverlapped)764 BOOL LockFileEx(HANDLE hFile, DWORD dwFlags, DWORD dwReserved, DWORD nNumberOfBytesToLockLow,
765                 DWORD nNumberOfBytesToLockHigh, LPOVERLAPPED lpOverlapped)
766 {
767 	ULONG Type;
768 	WINPR_HANDLE* handle;
769 
770 	if (hFile == INVALID_HANDLE_VALUE)
771 		return FALSE;
772 
773 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
774 		return FALSE;
775 
776 	handle = (WINPR_HANDLE*)hFile;
777 
778 	if (handle->ops->LockFileEx)
779 		return handle->ops->LockFileEx(handle, dwFlags, dwReserved, nNumberOfBytesToLockLow,
780 		                               nNumberOfBytesToLockHigh, lpOverlapped);
781 
782 	WLog_ERR(TAG, "LockFileEx operation not implemented");
783 	return FALSE;
784 }
785 
UnlockFile(HANDLE hFile,DWORD dwFileOffsetLow,DWORD dwFileOffsetHigh,DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh)786 BOOL UnlockFile(HANDLE hFile, DWORD dwFileOffsetLow, DWORD dwFileOffsetHigh,
787                 DWORD nNumberOfBytesToUnlockLow, DWORD nNumberOfBytesToUnlockHigh)
788 {
789 	ULONG Type;
790 	WINPR_HANDLE* handle;
791 
792 	if (hFile == INVALID_HANDLE_VALUE)
793 		return FALSE;
794 
795 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
796 		return FALSE;
797 
798 	handle = (WINPR_HANDLE*)hFile;
799 
800 	if (handle->ops->UnlockFile)
801 		return handle->ops->UnlockFile(handle, dwFileOffsetLow, dwFileOffsetHigh,
802 		                               nNumberOfBytesToUnlockLow, nNumberOfBytesToUnlockHigh);
803 
804 	WLog_ERR(TAG, "UnLockFile operation not implemented");
805 	return FALSE;
806 }
807 
UnlockFileEx(HANDLE hFile,DWORD dwReserved,DWORD nNumberOfBytesToUnlockLow,DWORD nNumberOfBytesToUnlockHigh,LPOVERLAPPED lpOverlapped)808 BOOL UnlockFileEx(HANDLE hFile, DWORD dwReserved, DWORD nNumberOfBytesToUnlockLow,
809                   DWORD nNumberOfBytesToUnlockHigh, LPOVERLAPPED lpOverlapped)
810 {
811 	ULONG Type;
812 	WINPR_HANDLE* handle;
813 
814 	if (hFile == INVALID_HANDLE_VALUE)
815 		return FALSE;
816 
817 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
818 		return FALSE;
819 
820 	handle = (WINPR_HANDLE*)hFile;
821 
822 	if (handle->ops->UnlockFileEx)
823 		return handle->ops->UnlockFileEx(handle, dwReserved, nNumberOfBytesToUnlockLow,
824 		                                 nNumberOfBytesToUnlockHigh, lpOverlapped);
825 
826 	WLog_ERR(TAG, "UnLockFileEx operation not implemented");
827 	return FALSE;
828 }
829 
SetFileTime(HANDLE hFile,const FILETIME * lpCreationTime,const FILETIME * lpLastAccessTime,const FILETIME * lpLastWriteTime)830 BOOL WINAPI SetFileTime(HANDLE hFile, const FILETIME* lpCreationTime,
831                         const FILETIME* lpLastAccessTime, const FILETIME* lpLastWriteTime)
832 {
833 	ULONG Type;
834 	WINPR_HANDLE* handle;
835 
836 	if (hFile == INVALID_HANDLE_VALUE)
837 		return FALSE;
838 
839 	if (!winpr_Handle_GetInfo(hFile, &Type, &handle))
840 		return FALSE;
841 
842 	handle = (WINPR_HANDLE*)hFile;
843 
844 	if (handle->ops->SetFileTime)
845 		return handle->ops->SetFileTime(handle, lpCreationTime, lpLastAccessTime, lpLastWriteTime);
846 
847 	WLog_ERR(TAG, "%s operation not implemented", __FUNCTION__);
848 	return FALSE;
849 }
850 
851 struct _WIN32_FILE_SEARCH
852 {
853 	DIR* pDir;
854 	LPSTR lpPath;
855 	LPSTR lpPattern;
856 	struct dirent* pDirent;
857 };
858 typedef struct _WIN32_FILE_SEARCH WIN32_FILE_SEARCH;
859 
FindDataFromStat(const char * path,const struct stat * fileStat,LPWIN32_FIND_DATAA lpFindFileData)860 static BOOL FindDataFromStat(const char* path, const struct stat* fileStat,
861                              LPWIN32_FIND_DATAA lpFindFileData)
862 {
863 	UINT64 ft;
864 	char* lastSep;
865 	lpFindFileData->dwFileAttributes = 0;
866 
867 	if (S_ISDIR(fileStat->st_mode))
868 		lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY;
869 
870 	if (lpFindFileData->dwFileAttributes == 0)
871 		lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE;
872 
873 	lastSep = strrchr(path, '/');
874 
875 	if (lastSep)
876 	{
877 		const char* name = lastSep + 1;
878 		const size_t namelen = strlen(name);
879 
880 		if ((namelen > 1) && (name[0] == '.') && (name[1] != '.'))
881 			lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN;
882 	}
883 
884 	if (!(fileStat->st_mode & S_IWUSR))
885 		lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_READONLY;
886 
887 #ifdef _DARWIN_FEATURE_64_BIT_INODE
888 	ft = STAT_TIME_TO_FILETIME(fileStat->st_birthtime);
889 #else
890 	ft = STAT_TIME_TO_FILETIME(fileStat->st_ctime);
891 #endif
892 	lpFindFileData->ftCreationTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
893 	lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF;
894 	ft = STAT_TIME_TO_FILETIME(fileStat->st_mtime);
895 	lpFindFileData->ftLastWriteTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
896 	lpFindFileData->ftLastWriteTime.dwLowDateTime = ft & 0xFFFFFFFF;
897 	ft = STAT_TIME_TO_FILETIME(fileStat->st_atime);
898 	lpFindFileData->ftLastAccessTime.dwHighDateTime = ((UINT64)ft) >> 32ULL;
899 	lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF;
900 	lpFindFileData->nFileSizeHigh = ((UINT64)fileStat->st_size) >> 32ULL;
901 	lpFindFileData->nFileSizeLow = fileStat->st_size & 0xFFFFFFFF;
902 	return TRUE;
903 }
904 
FindFirstFileA(LPCSTR lpFileName,LPWIN32_FIND_DATAA lpFindFileData)905 HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData)
906 {
907 	BOOL isDir = FALSE;
908 	struct stat fileStat;
909 	WIN32_FILE_SEARCH* pFileSearch;
910 
911 	if (!lpFindFileData || !lpFileName)
912 	{
913 		SetLastError(ERROR_BAD_ARGUMENTS);
914 		return INVALID_HANDLE_VALUE;
915 	}
916 
917 	ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
918 	pFileSearch = (WIN32_FILE_SEARCH*)calloc(1, sizeof(WIN32_FILE_SEARCH));
919 
920 	if (!pFileSearch)
921 	{
922 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
923 		return INVALID_HANDLE_VALUE;
924 	}
925 
926 	if (stat(lpFileName, &fileStat) >= 0)
927 	{
928 		isDir = (S_ISDIR(fileStat.st_mode) != 0);
929 	}
930 	else
931 		errno = 0;
932 
933 	if (isDir)
934 	{
935 		pFileSearch->lpPath = _strdup(lpFileName);
936 		pFileSearch->lpPattern = _strdup(".");
937 	}
938 	else
939 	{
940 		LPSTR p;
941 		size_t index;
942 		size_t length;
943 		/* Separate lpFileName into path and pattern components */
944 		p = strrchr(lpFileName, '/');
945 
946 		if (!p)
947 			p = strrchr(lpFileName, '\\');
948 
949 		index = (p - lpFileName);
950 		length = (p - lpFileName) + 1;
951 		pFileSearch->lpPath = (LPSTR)malloc(length + 1);
952 
953 		if (!pFileSearch->lpPath)
954 		{
955 			free(pFileSearch);
956 			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
957 			return INVALID_HANDLE_VALUE;
958 		}
959 
960 		CopyMemory(pFileSearch->lpPath, lpFileName, length);
961 		pFileSearch->lpPath[length] = '\0';
962 		length = strlen(lpFileName) - index;
963 		pFileSearch->lpPattern = (LPSTR)malloc(length + 1);
964 
965 		if (!pFileSearch->lpPattern)
966 		{
967 			free(pFileSearch->lpPath);
968 			free(pFileSearch);
969 			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
970 			return INVALID_HANDLE_VALUE;
971 		}
972 
973 		CopyMemory(pFileSearch->lpPattern, &lpFileName[index + 1], length);
974 		pFileSearch->lpPattern[length] = '\0';
975 
976 		/* Check if the path is a directory */
977 
978 		if (stat(pFileSearch->lpPath, &fileStat) < 0)
979 		{
980 			FindClose(pFileSearch);
981 			SetLastError(map_posix_err(errno));
982 			errno = 0;
983 			return INVALID_HANDLE_VALUE; /* stat error */
984 		}
985 
986 		if (S_ISDIR(fileStat.st_mode) == 0)
987 		{
988 			FindClose(pFileSearch);
989 			return INVALID_HANDLE_VALUE; /* not a directory */
990 		}
991 	}
992 
993 	/* Open directory for reading */
994 	pFileSearch->pDir = opendir(pFileSearch->lpPath);
995 
996 	if (!pFileSearch->pDir)
997 	{
998 		FindClose(pFileSearch);
999 		SetLastError(map_posix_err(errno));
1000 		errno = 0;
1001 		return INVALID_HANDLE_VALUE; /* failed to open directory */
1002 	}
1003 
1004 	if (FindNextFileA((HANDLE)pFileSearch, lpFindFileData))
1005 	{
1006 		if (isDir)
1007 		{
1008 			const char* name = strrchr(lpFileName, '/');
1009 
1010 			if (!name)
1011 				name = lpFileName;
1012 			else
1013 				name++;
1014 
1015 			pFileSearch->lpPattern[0] = '*';
1016 			sprintf_s(lpFindFileData->cFileName, ARRAYSIZE(lpFindFileData->cFileName), "%s", name);
1017 		}
1018 
1019 		return (HANDLE)pFileSearch;
1020 	}
1021 
1022 	FindClose(pFileSearch);
1023 	return INVALID_HANDLE_VALUE;
1024 }
1025 
ConvertFindDataAToW(LPWIN32_FIND_DATAA lpFindFileDataA,LPWIN32_FIND_DATAW lpFindFileDataW)1026 static BOOL ConvertFindDataAToW(LPWIN32_FIND_DATAA lpFindFileDataA,
1027                                 LPWIN32_FIND_DATAW lpFindFileDataW)
1028 {
1029 	size_t length;
1030 	WCHAR* unicodeFileName;
1031 
1032 	if (!lpFindFileDataA || !lpFindFileDataW)
1033 		return FALSE;
1034 
1035 	lpFindFileDataW->dwFileAttributes = lpFindFileDataA->dwFileAttributes;
1036 	lpFindFileDataW->ftCreationTime = lpFindFileDataA->ftCreationTime;
1037 	lpFindFileDataW->ftLastAccessTime = lpFindFileDataA->ftLastAccessTime;
1038 	lpFindFileDataW->ftLastWriteTime = lpFindFileDataA->ftLastWriteTime;
1039 	lpFindFileDataW->nFileSizeHigh = lpFindFileDataA->nFileSizeHigh;
1040 	lpFindFileDataW->nFileSizeLow = lpFindFileDataA->nFileSizeLow;
1041 	lpFindFileDataW->dwReserved0 = lpFindFileDataA->dwReserved0;
1042 	lpFindFileDataW->dwReserved1 = lpFindFileDataA->dwReserved1;
1043 	unicodeFileName = NULL;
1044 	length = ConvertToUnicode(CP_UTF8, 0, lpFindFileDataA->cFileName, -1, &unicodeFileName, 0);
1045 
1046 	if (length == 0)
1047 		return FALSE;
1048 
1049 	if (length > MAX_PATH)
1050 		length = MAX_PATH;
1051 
1052 	CopyMemory(lpFindFileDataW->cFileName, unicodeFileName, length * sizeof(WCHAR));
1053 	free(unicodeFileName);
1054 	length =
1055 	    ConvertToUnicode(CP_UTF8, 0, lpFindFileDataA->cAlternateFileName, -1, &unicodeFileName, 0);
1056 
1057 	if (length == 0)
1058 		return TRUE;
1059 
1060 	if (length > 14)
1061 		length = 14;
1062 
1063 	CopyMemory(lpFindFileDataW->cAlternateFileName, unicodeFileName, length * sizeof(WCHAR));
1064 	free(unicodeFileName);
1065 	return TRUE;
1066 }
1067 
FindFirstFileW(LPCWSTR lpFileName,LPWIN32_FIND_DATAW lpFindFileData)1068 HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData)
1069 {
1070 	LPSTR utfFileName = NULL;
1071 	HANDLE h;
1072 	LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA));
1073 
1074 	if (!fd)
1075 	{
1076 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1077 		return INVALID_HANDLE_VALUE;
1078 	}
1079 
1080 	if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &utfFileName, 0, NULL, NULL) <= 0)
1081 	{
1082 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1083 		free(fd);
1084 		return INVALID_HANDLE_VALUE;
1085 	}
1086 
1087 	h = FindFirstFileA(utfFileName, fd);
1088 	free(utfFileName);
1089 
1090 	if (h != INVALID_HANDLE_VALUE)
1091 	{
1092 		if (!ConvertFindDataAToW(fd, lpFindFileData))
1093 		{
1094 			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1095 			FindClose(h);
1096 			h = INVALID_HANDLE_VALUE;
1097 			goto out;
1098 		}
1099 	}
1100 
1101 out:
1102 	free(fd);
1103 	return h;
1104 }
1105 
FindFirstFileExA(LPCSTR lpFileName,FINDEX_INFO_LEVELS fInfoLevelId,LPVOID lpFindFileData,FINDEX_SEARCH_OPS fSearchOp,LPVOID lpSearchFilter,DWORD dwAdditionalFlags)1106 HANDLE FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
1107                         FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags)
1108 {
1109 	return INVALID_HANDLE_VALUE;
1110 }
1111 
FindFirstFileExW(LPCWSTR lpFileName,FINDEX_INFO_LEVELS fInfoLevelId,LPVOID lpFindFileData,FINDEX_SEARCH_OPS fSearchOp,LPVOID lpSearchFilter,DWORD dwAdditionalFlags)1112 HANDLE FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData,
1113                         FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags)
1114 {
1115 	return INVALID_HANDLE_VALUE;
1116 }
1117 
FindNextFileA(HANDLE hFindFile,LPWIN32_FIND_DATAA lpFindFileData)1118 BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData)
1119 {
1120 	WIN32_FILE_SEARCH* pFileSearch;
1121 	struct stat fileStat;
1122 	char* fullpath;
1123 	size_t pathlen;
1124 	size_t namelen;
1125 
1126 	if (!hFindFile || !lpFindFileData)
1127 		return FALSE;
1128 
1129 	if (hFindFile == INVALID_HANDLE_VALUE)
1130 		return FALSE;
1131 
1132 	ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA));
1133 	pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1134 
1135 	while ((pFileSearch->pDirent = readdir(pFileSearch->pDir)) != NULL)
1136 	{
1137 		if (FilePatternMatchA(pFileSearch->pDirent->d_name, pFileSearch->lpPattern))
1138 		{
1139 			BOOL success;
1140 			strncpy(lpFindFileData->cFileName, pFileSearch->pDirent->d_name, MAX_PATH);
1141 			namelen = strnlen(lpFindFileData->cFileName, MAX_PATH);
1142 			pathlen = strlen(pFileSearch->lpPath);
1143 			fullpath = (char*)malloc(pathlen + namelen + 2);
1144 
1145 			if (fullpath == NULL)
1146 			{
1147 				SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1148 				return FALSE;
1149 			}
1150 
1151 			memcpy(fullpath, pFileSearch->lpPath, pathlen);
1152 			/* Ensure path is terminated with a separator, but prevent
1153 			 * duplicate separators */
1154 			if (fullpath[pathlen - 1] != '/')
1155 				fullpath[pathlen++] = '/';
1156 			memcpy(fullpath + pathlen, pFileSearch->pDirent->d_name, namelen);
1157 			fullpath[pathlen + namelen] = 0;
1158 
1159 			if (stat(fullpath, &fileStat) != 0)
1160 			{
1161 				free(fullpath);
1162 				SetLastError(map_posix_err(errno));
1163 				errno = 0;
1164 				continue;
1165 			}
1166 
1167 			/* Skip FIFO entries. */
1168 			if (S_ISFIFO(fileStat.st_mode))
1169 			{
1170 				free(fullpath);
1171 				continue;
1172 			}
1173 
1174 			success = FindDataFromStat(fullpath, &fileStat, lpFindFileData);
1175 			free(fullpath);
1176 			return success;
1177 		}
1178 	}
1179 
1180 	SetLastError(ERROR_NO_MORE_FILES);
1181 	return FALSE;
1182 }
1183 
FindNextFileW(HANDLE hFindFile,LPWIN32_FIND_DATAW lpFindFileData)1184 BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData)
1185 {
1186 	LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)calloc(1, sizeof(WIN32_FIND_DATAA));
1187 
1188 	if (!fd)
1189 	{
1190 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1191 		return FALSE;
1192 	}
1193 
1194 	if (FindNextFileA(hFindFile, fd))
1195 	{
1196 		if (!ConvertFindDataAToW(fd, lpFindFileData))
1197 		{
1198 			SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1199 			free(fd);
1200 			return FALSE;
1201 		}
1202 
1203 		free(fd);
1204 		return TRUE;
1205 	}
1206 
1207 	free(fd);
1208 	return FALSE;
1209 }
1210 
FindClose(HANDLE hFindFile)1211 BOOL FindClose(HANDLE hFindFile)
1212 {
1213 	WIN32_FILE_SEARCH* pFileSearch = (WIN32_FILE_SEARCH*)hFindFile;
1214 
1215 	/* Since INVALID_HANDLE_VALUE != NULL the analyzer guesses that there
1216 	 * is a initialized HANDLE that is not freed properly.
1217 	 * Disable this return to stop confusing the analyzer. */
1218 #ifndef __clang_analyzer__
1219 	if (!pFileSearch || (pFileSearch == INVALID_HANDLE_VALUE))
1220 		return FALSE;
1221 #endif
1222 
1223 	free(pFileSearch->lpPath);
1224 	free(pFileSearch->lpPattern);
1225 
1226 	if (pFileSearch->pDir)
1227 		closedir(pFileSearch->pDir);
1228 
1229 	free(pFileSearch);
1230 	return TRUE;
1231 }
1232 
CreateDirectoryA(LPCSTR lpPathName,LPSECURITY_ATTRIBUTES lpSecurityAttributes)1233 BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1234 {
1235 	if (!mkdir(lpPathName, S_IRUSR | S_IWUSR | S_IXUSR))
1236 		return TRUE;
1237 
1238 	return FALSE;
1239 }
1240 
CreateDirectoryW(LPCWSTR lpPathName,LPSECURITY_ATTRIBUTES lpSecurityAttributes)1241 BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes)
1242 {
1243 	char* utfPathName = NULL;
1244 	BOOL ret;
1245 
1246 	if (ConvertFromUnicode(CP_UTF8, 0, lpPathName, -1, &utfPathName, 0, NULL, NULL) <= 0)
1247 	{
1248 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1249 		return FALSE;
1250 	}
1251 
1252 	ret = CreateDirectoryA(utfPathName, lpSecurityAttributes);
1253 	free(utfPathName);
1254 	return ret;
1255 }
1256 
RemoveDirectoryA(LPCSTR lpPathName)1257 BOOL RemoveDirectoryA(LPCSTR lpPathName)
1258 {
1259 	int ret = rmdir(lpPathName);
1260 
1261 	if (ret != 0)
1262 		SetLastError(map_posix_err(errno));
1263 	else
1264 		SetLastError(STATUS_SUCCESS);
1265 
1266 	return ret == 0;
1267 }
1268 
RemoveDirectoryW(LPCWSTR lpPathName)1269 BOOL RemoveDirectoryW(LPCWSTR lpPathName)
1270 {
1271 	char* utfPathName = NULL;
1272 	BOOL ret;
1273 
1274 	if (ConvertFromUnicode(CP_UTF8, 0, lpPathName, -1, &utfPathName, 0, NULL, NULL) <= 0)
1275 	{
1276 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1277 		return FALSE;
1278 	}
1279 
1280 	ret = RemoveDirectoryA(utfPathName);
1281 	free(utfPathName);
1282 	return ret;
1283 }
1284 
MoveFileExA(LPCSTR lpExistingFileName,LPCSTR lpNewFileName,DWORD dwFlags)1285 BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags)
1286 {
1287 	struct stat st;
1288 	int ret;
1289 	ret = stat(lpNewFileName, &st);
1290 
1291 	if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0)
1292 	{
1293 		if (ret == 0)
1294 		{
1295 			SetLastError(ERROR_ALREADY_EXISTS);
1296 			return FALSE;
1297 		}
1298 	}
1299 	else
1300 	{
1301 		if (ret == 0 && (st.st_mode & S_IWUSR) == 0)
1302 		{
1303 			SetLastError(ERROR_ACCESS_DENIED);
1304 			return FALSE;
1305 		}
1306 	}
1307 
1308 	ret = rename(lpExistingFileName, lpNewFileName);
1309 
1310 	if (ret != 0)
1311 		SetLastError(map_posix_err(errno));
1312 
1313 	return ret == 0;
1314 }
1315 
MoveFileExW(LPCWSTR lpExistingFileName,LPCWSTR lpNewFileName,DWORD dwFlags)1316 BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags)
1317 {
1318 	LPSTR lpCExistingFileName;
1319 	LPSTR lpCNewFileName;
1320 	BOOL ret;
1321 
1322 	if (ConvertFromUnicode(CP_UTF8, 0, lpExistingFileName, -1, &lpCExistingFileName, 0, NULL,
1323 	                       NULL) <= 0)
1324 	{
1325 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1326 		return FALSE;
1327 	}
1328 
1329 	if (ConvertFromUnicode(CP_UTF8, 0, lpNewFileName, -1, &lpCNewFileName, 0, NULL, NULL) <= 0)
1330 	{
1331 		free(lpCExistingFileName);
1332 		SetLastError(ERROR_NOT_ENOUGH_MEMORY);
1333 		return FALSE;
1334 	}
1335 
1336 	ret = MoveFileExA(lpCExistingFileName, lpCNewFileName, dwFlags);
1337 	free(lpCNewFileName);
1338 	free(lpCExistingFileName);
1339 	return ret;
1340 }
1341 
MoveFileA(LPCSTR lpExistingFileName,LPCSTR lpNewFileName)1342 BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName)
1343 {
1344 	return MoveFileExA(lpExistingFileName, lpNewFileName, 0);
1345 }
1346 
MoveFileW(LPCWSTR lpExistingFileName,LPCWSTR lpNewFileName)1347 BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName)
1348 {
1349 	return MoveFileExW(lpExistingFileName, lpNewFileName, 0);
1350 }
1351 
1352 #endif
1353 
1354 /* Extended API */
1355 
UnixChangeFileMode(const char * filename,int flags)1356 int UnixChangeFileMode(const char* filename, int flags)
1357 {
1358 #ifndef _WIN32
1359 	mode_t fl = 0;
1360 	fl |= (flags & 0x4000) ? S_ISUID : 0;
1361 	fl |= (flags & 0x2000) ? S_ISGID : 0;
1362 	fl |= (flags & 0x1000) ? S_ISVTX : 0;
1363 	fl |= (flags & 0x0400) ? S_IRUSR : 0;
1364 	fl |= (flags & 0x0200) ? S_IWUSR : 0;
1365 	fl |= (flags & 0x0100) ? S_IXUSR : 0;
1366 	fl |= (flags & 0x0040) ? S_IRGRP : 0;
1367 	fl |= (flags & 0x0020) ? S_IWGRP : 0;
1368 	fl |= (flags & 0x0010) ? S_IXGRP : 0;
1369 	fl |= (flags & 0x0004) ? S_IROTH : 0;
1370 	fl |= (flags & 0x0002) ? S_IWOTH : 0;
1371 	fl |= (flags & 0x0001) ? S_IXOTH : 0;
1372 	return chmod(filename, fl);
1373 #else
1374 	int rc;
1375 	WCHAR* wfl = NULL;
1376 	int fl = 0;
1377 
1378 	if (ConvertToUnicode(CP_UTF8, 0, filename, -1, &wfl, 0) <= 0)
1379 		return -1;
1380 
1381 	/* Check for unsupported flags. */
1382 	if (flags & ~(_S_IREAD | _S_IWRITE))
1383 		WLog_WARN(TAG, "Unsupported file mode %d for _wchmod", flags);
1384 
1385 	rc = _wchmod(wfl, flags);
1386 	free(wfl);
1387 	return rc;
1388 #endif
1389 }
1390