diff --git a/channels/drive/client/CMakeLists.txt b/channels/drive/client/CMakeLists.txt index ba2d0f4f9..2c2be3930 100644 --- a/channels/drive/client/CMakeLists.txt +++ b/channels/drive/client/CMakeLists.txt @@ -22,13 +22,6 @@ set(${MODULE_PREFIX}_SRCS drive_file.h drive_main.c) -if(WIN32) - set(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_SRCS} - statvfs.c - statvfs.h - dirent.h) -endif() - add_channel_client_library(${MODULE_PREFIX} ${MODULE_NAME} ${CHANNEL_NAME} TRUE "DeviceServiceEntry") diff --git a/channels/drive/client/dirent.h b/channels/drive/client/dirent.h deleted file mode 100644 index 7c61d6a2c..000000000 --- a/channels/drive/client/dirent.h +++ /dev/null @@ -1,374 +0,0 @@ -/***************************************************************************** - * dirent.h - dirent API for Microsoft Visual Studio - * - * Copyright (C) 2006 Toni Ronkko - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * ``Software''), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL TONI RONKKO BE LIABLE FOR ANY CLAIM, DAMAGES OR - * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Mar 15, 2011, Toni Ronkko - * Defined FILE_ATTRIBUTE_DEVICE for MSVC 6.0. - * - * Aug 11, 2010, Toni Ronkko - * Added d_type and d_namlen fields to dirent structure. The former is - * especially useful for determining whether directory entry represents a - * file or a directory. For more information, see - * http://www.delorie.com/gnu/docs/glibc/libc_270.html - * - * Aug 11, 2010, Toni Ronkko - * Improved conformance to the standards. For example, errno is now set - * properly on failure and assert() is never used. Thanks to Peter Brockam - * for suggestions. - * - * Aug 11, 2010, Toni Ronkko - * Fixed a bug in rewinddir(): when using relative directory names, change - * of working directory no longer causes rewinddir() to fail. - * - * Dec 15, 2009, John Cunningham - * Added rewinddir member function - * - * Jan 18, 2008, Toni Ronkko - * Using FindFirstFileA and WIN32_FIND_DATAA to avoid converting string - * between multi-byte and unicode representations. This makes the - * code simpler and also allows the code to be compiled under MingW. Thanks - * to Azriel Fasten for the suggestion. - * - * Mar 4, 2007, Toni Ronkko - * Bug fix: due to the strncpy_s() function this file only compiled in - * Visual Studio 2005. Using the new string functions only when the - * compiler version allows. - * - * Nov 2, 2006, Toni Ronkko - * Major update: removed support for Watcom C, MS-DOS and Turbo C to - * simplify the file, updated the code to compile cleanly on Visual - * Studio 2005 with both unicode and multi-byte character strings, - * removed rewinddir() as it had a bug. - * - * Aug 20, 2006, Toni Ronkko - * Removed all remarks about MSVC 1.0, which is antiqued now. Simplified - * comments by removing SGML tags. - * - * May 14 2002, Toni Ronkko - * Embedded the function definitions directly to the header so that no - * source modules need to be included in the Visual Studio project. Removed - * all the dependencies to other projects so that this very header can be - * used independently. - * - * May 28 1998, Toni Ronkko - * First version. - *****************************************************************************/ -#ifndef DIRENT_H -#define DIRENT_H - -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#include -#include -#include -#include -#include -#include - -/* Entries missing from MSVC 6.0 */ -#if !defined(FILE_ATTRIBUTE_DEVICE) -# define FILE_ATTRIBUTE_DEVICE 0x40 -#endif - -/* File type and permission flags for stat() */ -#if defined(_MSC_VER) && !defined(S_IREAD) -# define S_IFMT _S_IFMT /* file type mask */ -# define S_IFDIR _S_IFDIR /* directory */ -# define S_IFCHR _S_IFCHR /* character device */ -# define S_IFFIFO _S_IFFIFO /* pipe */ -# define S_IFREG _S_IFREG /* regular file */ -# define S_IREAD _S_IREAD /* read permission */ -# define S_IWRITE _S_IWRITE /* write permission */ -# define S_IEXEC _S_IEXEC /* execute permission */ -#endif -#define S_IFBLK 0 /* block device */ -#define S_IFLNK 0 /* link */ -#define S_IFSOCK 0 /* socket */ - -#if defined(_MSC_VER) -# define S_IRUSR S_IREAD /* read, user */ -# define S_IWUSR S_IWRITE /* write, user */ -# define S_IXUSR 0 /* execute, user */ -# define S_IRGRP 0 /* read, group */ -# define S_IWGRP 0 /* write, group */ -# define S_IXGRP 0 /* execute, group */ -# define S_IROTH 0 /* read, others */ -# define S_IWOTH 0 /* write, others */ -# define S_IXOTH 0 /* execute, others */ -#endif - -/* Indicates that d_type field is available in dirent structure */ -#define _DIRENT_HAVE_D_TYPE - -/* File type flags for d_type */ -#define DT_UNKNOWN 0 -#define DT_REG S_IFREG -#define DT_DIR S_IFDIR -#define DT_FIFO S_IFFIFO -#define DT_SOCK S_IFSOCK -#define DT_CHR S_IFCHR -#define DT_BLK S_IFBLK - -/* Macros for converting between st_mode and d_type */ -#define IFTODT(mode) ((mode) & S_IFMT) -#define DTTOIF(type) (type) - -/* - * File type macros. Note that block devices, sockets and links cannot be - * distinguished on Windows and the macros S_ISBLK, S_ISSOCK and S_ISLNK are - * only defined for compatibility. These macros should always return FALSE - * on Windows. - */ -#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFFIFO) -#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) -#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) -#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) -#define S_ISSOCK(mode) (((mode) & S_IFMT) == S_IFSOCK) -#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) -#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) - -#ifdef __cplusplus -extern "C" { -#endif - - -typedef struct dirent -{ - char d_name[MAX_PATH + 1]; /* File name */ - size_t d_namlen; /* Length of name without \0 */ - int d_type; /* File type */ -} dirent; - - -typedef struct DIR -{ - dirent curentry; /* Current directory entry */ - WIN32_FIND_DATAA find_data; /* Private file data */ - int cached; /* True if data is valid */ - HANDLE search_handle; /* Win32 search handle */ - char patt[MAX_PATH + 3]; /* Initial directory name */ -} DIR; - - -/* Forward declarations */ -static DIR *opendir(const char *dirname); -static struct dirent *readdir(DIR *dirp); -static int closedir(DIR *dirp); -static void rewinddir(DIR* dirp); - - -/* Use the new safe string functions introduced in Visual Studio 2005 */ -#if defined(_MSC_VER) && _MSC_VER >= 1400 -# define DIRENT_STRNCPY(dest,src,size) strncpy_s((dest),(size),(src),_TRUNCATE) -#else -# define DIRENT_STRNCPY(dest,src,size) strncpy((dest),(src),(size)) -#endif - -/* Set errno variable */ -#if defined(_MSC_VER) -#define DIRENT_SET_ERRNO(x) _set_errno (x) -#else -#define DIRENT_SET_ERRNO(x) (errno = (x)) -#endif - - -/***************************************************************************** - * Open directory stream DIRNAME for read and return a pointer to the - * internal working area that is used to retrieve individual directory - * entries. - */ -static DIR *opendir(const char *dirname) -{ - DIR *dirp; - - /* ensure that the resulting search pattern will be a valid file name */ - if (dirname == NULL) { - DIRENT_SET_ERRNO (ENOENT); - return NULL; - } - if (strlen (dirname) + 3 >= MAX_PATH) { - DIRENT_SET_ERRNO (ENAMETOOLONG); - return NULL; - } - - /* construct new DIR structure */ - dirp = (DIR*) malloc (sizeof (struct DIR)); - if (dirp != NULL) { - int error; - - /* - * Convert relative directory name to an absolute one. This - * allows rewinddir() to function correctly when the current working - * directory is changed between opendir() and rewinddir(). - */ - if (GetFullPathNameA(dirname, MAX_PATH, dirp->patt, NULL)) { - char *p; - - /* append the search pattern "\\*\0" to the directory name */ - p = strchr (dirp->patt, '\0'); - if (dirp->patt < p && *(p-1) != '\\' && *(p-1) != ':') { - *p++ = '\\'; - } - *p++ = '*'; - *p = '\0'; - - /* open directory stream and retrieve the first entry */ - dirp->search_handle = FindFirstFileA(dirp->patt, &dirp->find_data); - if (dirp->search_handle != INVALID_HANDLE_VALUE) { - /* a directory entry is now waiting in memory */ - dirp->cached = 1; - error = 0; - } else { - /* search pattern is not a directory name? */ - DIRENT_SET_ERRNO (ENOENT); - error = 1; - } - } else { - /* buffer too small */ - DIRENT_SET_ERRNO (ENOMEM); - error = 1; - } - - if (error) { - free (dirp); - dirp = NULL; - } - } - - return dirp; -} - - -/***************************************************************************** - * Read a directory entry, and return a pointer to a dirent structure - * containing the name of the entry in d_name field. Individual directory - * entries returned by this very function include regular files, - * sub-directories, pseudo-directories "." and "..", but also volume labels, - * hidden files and system files may be returned. - */ -static struct dirent *readdir(DIR *dirp) -{ - DWORD attr; - if (dirp == NULL) { - /* directory stream did not open */ - DIRENT_SET_ERRNO (EBADF); - return NULL; - } - - /* get next directory entry */ - if (dirp->cached != 0) { - /* a valid directory entry already in memory */ - dirp->cached = 0; - } else { - /* get the next directory entry from stream */ - if (dirp->search_handle == INVALID_HANDLE_VALUE) { - return NULL; - } - if (FindNextFileA (dirp->search_handle, &dirp->find_data) == FALSE) { - /* the very last entry has been processed or an error occurred */ - FindClose (dirp->search_handle); - dirp->search_handle = INVALID_HANDLE_VALUE; - return NULL; - } - } - - /* copy as a multibyte character string */ - DIRENT_STRNCPY ( dirp->curentry.d_name, - dirp->find_data.cFileName, - sizeof(dirp->curentry.d_name) ); - dirp->curentry.d_name[MAX_PATH] = '\0'; - - /* compute the length of name */ - dirp->curentry.d_namlen = strlen (dirp->curentry.d_name); - - /* determine file type */ - attr = dirp->find_data.dwFileAttributes; - if ((attr & FILE_ATTRIBUTE_DEVICE) != 0) { - dirp->curentry.d_type = DT_CHR; - } else if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - dirp->curentry.d_type = DT_DIR; - } else { - dirp->curentry.d_type = DT_REG; - } - return &dirp->curentry; -} - - -/***************************************************************************** - * Close directory stream opened by opendir() function. Close of the - * directory stream invalidates the DIR structure as well as any previously - * read directory entry. - */ -static int closedir(DIR *dirp) -{ - if (dirp == NULL) { - /* invalid directory stream */ - DIRENT_SET_ERRNO (EBADF); - return -1; - } - - /* release search handle */ - if (dirp->search_handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->search_handle); - dirp->search_handle = INVALID_HANDLE_VALUE; - } - - /* release directory structure */ - free (dirp); - return 0; -} - - -/***************************************************************************** - * Resets the position of the directory stream to which dirp refers to the - * beginning of the directory. It also causes the directory stream to refer - * to the current state of the corresponding directory, as a call to opendir() - * would have done. If dirp does not refer to a directory stream, the effect - * is undefined. - */ -static void rewinddir(DIR* dirp) -{ - if (dirp != NULL) { - /* release search handle */ - if (dirp->search_handle != INVALID_HANDLE_VALUE) { - FindClose (dirp->search_handle); - } - - /* open new search handle and retrieve the first entry */ - dirp->search_handle = FindFirstFileA (dirp->patt, &dirp->find_data); - if (dirp->search_handle != INVALID_HANDLE_VALUE) { - /* a directory entry is now waiting in memory */ - dirp->cached = 1; - } else { - /* failed to re-open directory: no directory entry in memory */ - dirp->cached = 0; - } - } -} - - -#ifdef __cplusplus -} -#endif -#endif /*DIRENT_H*/ diff --git a/channels/drive/client/drive_file.c b/channels/drive/client/drive_file.c index bf1114e5f..5e3614a0b 100644 --- a/channels/drive/client/drive_file.c +++ b/channels/drive/client/drive_file.c @@ -44,6 +44,7 @@ #include #include +#include #include #include #include @@ -51,23 +52,6 @@ #include -#ifdef HAVE_UNISTD_H -#include -#endif - -#ifdef HAVE_FCNTL_H -#define __USE_GNU /* for O_PATH */ -#include -#undef __USE_GNU -#endif - -#ifdef _WIN32 -#pragma comment(lib, "Shlwapi.lib") -#include -#else -#include -#endif - #include "drive_file.h" #ifdef _WIN32 @@ -75,10 +59,18 @@ #pragma warning(disable: 4244) #endif -static void drive_file_fix_path(char* path) +#ifdef WITH_DEBUG_RDPDR +#define DEBUG_WSTR(msg, wstr) do { LPSTR lpstr; ConvertFromUnicode(CP_UTF8, 0, wstr, -1, &lpstr, 0, NULL, NULL); WLog_DBG(TAG, msg, lpstr); free(lpstr); } while (0) +#else +#define DEBUG_WSTR(msg, wstr) do { } while (0) +#endif + + +static void drive_file_fix_path(WCHAR* path) { - size_t i; - size_t length = strlen(path); + int i; + int length; + length = (int) _wcslen(path); for (i = 0; i < length; i++) { @@ -102,14 +94,17 @@ static void drive_file_fix_path(char* path) path[length - 1] = '\0'; } -static char* drive_file_combine_fullpath(const char* base_path, const char* path) +static WCHAR* drive_file_combine_fullpath(const WCHAR* base_path, const WCHAR* path, + UINT32 PathLength) { - char* fullpath; + WCHAR* fullpath; + UINT32 base_path_length; if (!base_path || !path) return NULL; - fullpath = (char*) malloc(strlen(base_path) + strlen(path) + 1); + base_path_length = _wcslen(base_path) * 2; + fullpath = (WCHAR*)calloc(1, base_path_length + PathLength + sizeof(WCHAR)); if (!fullpath) { @@ -117,91 +112,94 @@ static char* drive_file_combine_fullpath(const char* base_path, const char* path return NULL; } - strcpy(fullpath, base_path); - strcat(fullpath, path); + CopyMemory(fullpath, base_path, base_path_length); + CopyMemory((char*)fullpath + base_path_length, path, PathLength); drive_file_fix_path(fullpath); return fullpath; } -static BOOL drive_file_remove_dir(const char* path) +static BOOL drive_file_remove_dir(const WCHAR* path) { - DIR* dir; - char* p; - struct STAT st; - struct dirent* pdirent; + WIN32_FIND_DATAW findFileData; BOOL ret = TRUE; + INT len; + HANDLE dir; + WCHAR* fullpath; + WCHAR* path_slash; + UINT32 base_path_length; + base_path_length = _wcslen(path) * 2; + path_slash = (WCHAR*)calloc(1, base_path_length + sizeof(WCHAR) * 3); + + if (!path_slash) + { + WLog_ERR(TAG, "malloc failed!"); + return FALSE; + } if (!path) return FALSE; - dir = opendir(path); + CopyMemory(path_slash, path, base_path_length); + path_slash[base_path_length / 2] = '/'; + path_slash[base_path_length / 2 + 1] = '*'; + DEBUG_WSTR("Search in %s", path_slash); + dir = FindFirstFileW(path_slash, &findFileData); + path_slash[base_path_length / 2 + 1] = 0; - if (dir == NULL) - return FALSE; - - pdirent = readdir(dir); - - while (pdirent) + if (dir == INVALID_HANDLE_VALUE) { - if (strcmp(pdirent->d_name, ".") == 0 || strcmp(pdirent->d_name, "..") == 0) + free(path_slash); + return FALSE; + } + + do + { + len = _wcslen(findFileData.cFileName); + + if ((len == 1 && findFileData.cFileName[0] == '.') || (len == 2 && + findFileData.cFileName[0] == '.' && findFileData.cFileName[1] == '.')) { - pdirent = readdir(dir); continue; } - p = (char*) malloc(strlen(path) + strlen(pdirent->d_name) + 2); + fullpath = drive_file_combine_fullpath(path_slash, findFileData.cFileName, len * 2); + DEBUG_WSTR("Delete %s", fullpath); - if (!p) + if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { - WLog_ERR(TAG, "malloc failed!"); - return FALSE; - } - - sprintf(p, "%s/%s", path, pdirent->d_name); - - if (STAT(p, &st) != 0) - { - ret = FALSE; - } - else if (S_ISDIR(st.st_mode)) - { - ret = drive_file_remove_dir(p); - } - else if (unlink(p) < 0) - { - ret = FALSE; + ret = drive_file_remove_dir(fullpath); } else { - ret = TRUE; + ret = DeleteFileW(fullpath); } - free(p); + free(fullpath); if (!ret) break; - - pdirent = readdir(dir); } + while (ret && FindNextFileW(dir, &findFileData) != 0); - closedir(dir); + FindClose(dir); if (ret) { - if (rmdir(path) < 0) + if (!RemoveDirectoryW(path)) { ret = FALSE; } } + free(path_slash); return ret; } -static void drive_file_set_fullpath(DRIVE_FILE* file, char* fullpath) +static void drive_file_set_fullpath(DRIVE_FILE* file, WCHAR* fullpath) { free(file->fullpath); file->fullpath = fullpath; - file->filename = strrchr(file->fullpath, '/'); + file->filename = _wcsrchr(file->fullpath, 0x5c); if (file->filename == NULL) file->filename = file->fullpath; @@ -209,139 +207,126 @@ static void drive_file_set_fullpath(DRIVE_FILE* file, char* fullpath) file->filename += 1; } -static BOOL drive_file_init(DRIVE_FILE* file, UINT32 DesiredAccess, UINT32 CreateDisposition, - UINT32 CreateOptions) +BOOL drive_file_init(DRIVE_FILE* file) { - struct STAT st; - BOOL exists; -#ifdef WIN32 - const static int mode = _S_IREAD | _S_IWRITE ; -#else - const static int mode = S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH; - BOOL largeFile = FALSE; -#endif - int oflag = 0; + UINT CreateDisposition = 0; + DWORD dwAttr = GetFileAttributesW(file->fullpath); - if (STAT(file->fullpath, &st) == 0) + if (dwAttr != INVALID_FILE_ATTRIBUTES) { - file->is_dir = (S_ISDIR(st.st_mode) ? TRUE : FALSE); + /* The file exists */ + file->is_dir = (dwAttr & FILE_ATTRIBUTE_DIRECTORY) != 0; - if (!file->is_dir && !S_ISREG(st.st_mode)) + if (file->is_dir) { - file->err = EPERM; + if (file->CreateDisposition == FILE_CREATE) + { + SetLastError(ERROR_ALREADY_EXISTS); + return FALSE; + } + + if (file->CreateOptions & FILE_NON_DIRECTORY_FILE) + { + SetLastError(ERROR_ACCESS_DENIED); + return FALSE; + } + return TRUE; } - -#ifndef WIN32 - - if (st.st_size > (unsigned long) 0x07FFFFFFF) - largeFile = TRUE; - -#endif - exists = TRUE; + else + { + if (file->CreateOptions & FILE_DIRECTORY_FILE) + { + SetLastError(ERROR_DIRECTORY); + return FALSE; + } + } } else { - file->is_dir = ((CreateOptions & FILE_DIRECTORY_FILE) ? TRUE : FALSE); + file->is_dir = ((file->CreateOptions & FILE_DIRECTORY_FILE) ? TRUE : FALSE); if (file->is_dir) { /* Should only create the directory if the disposition allows for it */ - if ((CreateDisposition == FILE_OPEN_IF) || (CreateDisposition == FILE_CREATE)) + if ((file->CreateDisposition == FILE_OPEN_IF) || (file->CreateDisposition == FILE_CREATE)) { - if (mkdir(file->fullpath, mode) != 0) + if (CreateDirectoryW(file->fullpath, NULL) != 0) { - file->err = errno; return TRUE; } } - } - exists = FALSE; - } - - if (file->is_dir) - { - file->dir = opendir(file->fullpath); - - if (file->dir == NULL) - { - file->err = errno; - return TRUE; + SetLastError(ERROR_FILE_NOT_FOUND); + return FALSE; } } - else + + if (file->file_handle == INVALID_HANDLE_VALUE) { - switch (CreateDisposition) + switch (file->CreateDisposition) { - case FILE_SUPERSEDE: - oflag = O_TRUNC | O_CREAT; + case FILE_SUPERSEDE: /* If the file already exists, replace it with the given file. If it does not, create the given file. */ + CreateDisposition = CREATE_ALWAYS; break; - case FILE_OPEN: + case FILE_OPEN: /* If the file already exists, open it instead of creating a new file. If it does not, fail the request and do not create a new file. */ + CreateDisposition = OPEN_EXISTING; break; - case FILE_CREATE: - oflag = O_CREAT | O_EXCL; + case FILE_CREATE: /* If the file already exists, fail the request and do not create or open the given file. If it does not, create the given file. */ + CreateDisposition = CREATE_NEW; break; - case FILE_OPEN_IF: - oflag = O_CREAT; + case FILE_OPEN_IF: /* If the file already exists, open it. If it does not, create the given file. */ + CreateDisposition = OPEN_ALWAYS; break; - case FILE_OVERWRITE: - oflag = O_TRUNC; + case FILE_OVERWRITE: /* If the file already exists, open it and overwrite it. If it does not, fail the request. */ + CreateDisposition = TRUNCATE_EXISTING; break; - case FILE_OVERWRITE_IF: - oflag = O_TRUNC | O_CREAT; + case FILE_OVERWRITE_IF: /* If the file already exists, open it and overwrite it. If it does not, create the given file. */ + CreateDisposition = CREATE_ALWAYS; break; default: break; } - if ((CreateOptions & FILE_DELETE_ON_CLOSE) && (DesiredAccess & DELETE)) - { - file->delete_pending = TRUE; - } - - if ((DesiredAccess & GENERIC_ALL) - || (DesiredAccess & GENERIC_WRITE) - || (DesiredAccess & FILE_WRITE_DATA) - || (DesiredAccess & FILE_APPEND_DATA)) - { - oflag |= O_RDWR; - } - else - { - oflag |= O_RDONLY; - } - #ifndef WIN32 - - if (largeFile) - { - oflag |= O_LARGEFILE; - } - -#else - oflag |= O_BINARY; + file->SharedAccess = 0; #endif - file->fd = OPEN(file->fullpath, oflag, mode); + file->file_handle = CreateFileW(file->fullpath, file->DesiredAccess, + file->SharedAccess, NULL, CreateDisposition, + file->FileAttributes, NULL); + } - if (file->fd == -1) + if (file->file_handle == INVALID_HANDLE_VALUE) + { + /* Get the error message, if any. */ + DWORD errorMessageID = GetLastError(); + + if (errorMessageID != 0) { - file->err = errno; - return TRUE; +#ifdef WIN32 + LPSTR messageBuffer = NULL; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + WLog_ERR(TAG, "Error in drive_file_init: %s %s", messageBuffer, file->fullpath); + /* Free the buffer. */ + LocalFree(messageBuffer); +#endif } } - return TRUE; + return file->file_handle != INVALID_HANDLE_VALUE; } -DRIVE_FILE* drive_file_new(const char* base_path, const char* path, UINT32 id, - UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions) +DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id, + UINT32 DesiredAccess, UINT32 CreateDisposition, + UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess) { DRIVE_FILE* file; file = (DRIVE_FILE*) calloc(1, sizeof(DRIVE_FILE)); @@ -352,116 +337,114 @@ DRIVE_FILE* drive_file_new(const char* base_path, const char* path, UINT32 id, return NULL; } - file->id = id; - file->basepath = (char*) base_path; - drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path)); - file->fd = -1; + if (DesiredAccess & 0x1000L) + { + DesiredAccess = (DesiredAccess & ~0x1000L) | GENERIC_WRITE; + } - if (!drive_file_init(file, DesiredAccess, CreateDisposition, CreateOptions)) + file->file_handle = INVALID_HANDLE_VALUE; + file->find_handle = INVALID_HANDLE_VALUE; + file->id = id; + file->basepath = (WCHAR*) base_path; + file->FileAttributes = FileAttributes; + file->DesiredAccess = DesiredAccess; + file->CreateDisposition = CreateDisposition; + file->CreateOptions = CreateOptions; + file->SharedAccess = SharedAccess; + drive_file_set_fullpath(file, drive_file_combine_fullpath(base_path, path, PathLength)); + + if (!drive_file_init(file)) { drive_file_free(file); return NULL; } -#if defined(__linux__) && defined(O_PATH) - - if (file->fd < 0 && file->err == EACCES) - { - /** - * We have no access permissions for the file or directory but if the - * peer is only interested in reading the object's attributes we can try - * to obtain a file descriptor who's only purpose is to perform - * operations that act purely at the file descriptor level. - * See open(2) - **/ - { - if ((file->fd = OPEN(file->fullpath, O_PATH)) >= 0) - { - file->err = 0; - } - } - } - -#endif return file; } -void drive_file_free(DRIVE_FILE* file) +BOOL drive_file_free(DRIVE_FILE* file) { if (!file) - return; + return FALSE; - if (file->fd != -1) - close(file->fd); + if (file->file_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(file->file_handle); + file->file_handle = INVALID_HANDLE_VALUE; + } - if (file->dir != NULL) - closedir(file->dir); + if (file->find_handle != INVALID_HANDLE_VALUE) + { + FindClose(file->find_handle); + file->find_handle = INVALID_HANDLE_VALUE; + } if (file->delete_pending) { if (file->is_dir) drive_file_remove_dir(file->fullpath); - else - unlink(file->fullpath); + else if (!DeleteFileW(file->fullpath)) + { + free(file->fullpath); + free(file); + return FALSE; + } } - free(file->pattern); + DEBUG_WSTR("Free %s", file->fullpath); free(file->fullpath); free(file); + return TRUE; } BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset) { + LONG lDistHigh; + DWORD dwPtrLow; + if (!file) return FALSE; - if (file->is_dir || file->fd == -1) - return FALSE; - - if (LSEEK(file->fd, Offset, SEEK_SET) == (off_t) - 1) - return FALSE; - - return TRUE; + lDistHigh = Offset >> 32; + DEBUG_WSTR("Seek %s", file->fullpath); + dwPtrLow = SetFilePointer(file->file_handle, Offset & 0xFFFFFFFF, &lDistHigh, FILE_BEGIN); + return dwPtrLow != INVALID_SET_FILE_POINTER; } BOOL drive_file_read(DRIVE_FILE* file, BYTE* buffer, UINT32* Length) { - ssize_t r; + UINT32 read; if (!file || !buffer || !Length) return FALSE; - if (file->is_dir || file->fd == -1) - return FALSE; + DEBUG_WSTR("Read file %s", file->fullpath); - r = read(file->fd, buffer, *Length); + if (ReadFile(file->file_handle, buffer, *Length, &read, NULL)) + { + *Length = read; + return TRUE; + } - if (r < 0) - return FALSE; - - *Length = (UINT32) r; - return TRUE; + return FALSE; } BOOL drive_file_write(DRIVE_FILE* file, BYTE* buffer, UINT32 Length) { - ssize_t r; + UINT32 written; if (!file || !buffer) return FALSE; - if (file->is_dir || file->fd == -1) - return FALSE; + DEBUG_WSTR("Write file %s", file->fullpath); while (Length > 0) { - r = write(file->fd, buffer, Length); - - if (r == -1) + if (!WriteFile(file->file_handle, buffer, Length, &written, NULL)) return FALSE; - Length -= r; - buffer += r; + Length -= written; + buffer += written; } return TRUE; @@ -469,17 +452,31 @@ BOOL drive_file_write(DRIVE_FILE* file, BYTE* buffer, UINT32 Length) BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, wStream* output) { - struct STAT st; + WIN32_FIND_DATAW findFileData; + HANDLE hFind; + DEBUG_WSTR("FindFirstFile %s", file->fullpath); if (!file || !output) return FALSE; - if (STAT(file->fullpath, &st) != 0) + if ((hFind = FindFirstFileW(file->fullpath, &findFileData)) == INVALID_HANDLE_VALUE) { - Stream_Write_UINT32(output, 0); /* Length */ - return FALSE; +#ifdef WIN32 + ZeroMemory(&findFileData, sizeof(findFileData)); + findFileData.dwFileAttributes = GetFileAttributesW(file->fullpath); + + if (findFileData.dwFileAttributes == INVALID_FILE_ATTRIBUTES) + { + goto out_fail; + } + +#else + goto out_fail; +#endif } + FindClose(hFind); + switch (FsInformationClass) { case FileBasicInformation: @@ -489,11 +486,15 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w goto out_fail; Stream_Write_UINT32(output, 36); /* Length */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ - Stream_Write_UINT32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + Stream_Write_UINT32(output, findFileData.ftCreationTime.dwLowDateTime); /* CreationTime */ + Stream_Write_UINT32(output, findFileData.ftCreationTime.dwHighDateTime); /* CreationTime */ + Stream_Write_UINT32(output, findFileData.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, findFileData.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, findFileData.ftLastWriteTime.dwHighDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, findFileData.dwFileAttributes); /* FileAttributes */ /* Reserved(4), MUST NOT be added! */ break; @@ -504,11 +505,14 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w goto out_fail; Stream_Write_UINT32(output, 22); /* Length */ - Stream_Write_UINT64(output, st.st_size); /* AllocationSize */ - Stream_Write_UINT64(output, st.st_size); /* EndOfFile */ - Stream_Write_UINT32(output, st.st_nlink); /* NumberOfLinks */ + Stream_Write_UINT32(output, findFileData.nFileSizeLow); /* AllocationSize */ + Stream_Write_UINT32(output, findFileData.nFileSizeHigh); /* AllocationSize */ + Stream_Write_UINT32(output, findFileData.nFileSizeLow); /* EndOfFile */ + Stream_Write_UINT32(output, findFileData.nFileSizeHigh); /* EndOfFile */ + Stream_Write_UINT32(output, 0); /* NumberOfLinks */ Stream_Write_UINT8(output, file->delete_pending ? 1 : 0); /* DeletePending */ - Stream_Write_UINT8(output, file->is_dir ? 1 : 0); /* Directory */ + Stream_Write_UINT8(output, findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? TRUE : + FALSE); /* Directory */ /* Reserved(2), MUST NOT be added! */ break; @@ -519,14 +523,13 @@ BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, w goto out_fail; Stream_Write_UINT32(output, 8); /* Length */ - Stream_Write_UINT32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + Stream_Write_UINT32(output, findFileData.dwFileAttributes); /* FileAttributes */ Stream_Write_UINT32(output, 0); /* ReparseTag */ break; default: /* Unhandled FsInformationClass */ - Stream_Write_UINT32(output, 0); /* Length */ - return FALSE; + goto out_fail; } return TRUE; @@ -535,38 +538,11 @@ out_fail: return FALSE; } -int dir_empty(const char* path) -{ -#ifdef _WIN32 - return PathIsDirectoryEmptyA(path); -#else - struct dirent* dp; - int empty = 1; - DIR* dir = opendir(path); - - if (dir == NULL) //Not a directory or doesn't exist - return 1; - - while ((dp = readdir(dir)) != NULL) - { - if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) - continue; /* Skip . and .. */ - - empty = 0; - break; - } - - closedir(dir); - return empty; -#endif -} BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, wStream* input) { - char* s = NULL; INT64 size; - int status; - char* fullpath; + WCHAR* fullpath; ULARGE_INTEGER liCreationTime; ULARGE_INTEGER liLastAccessTime; ULARGE_INTEGER liLastWriteTime; @@ -579,8 +555,10 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN FILETIME* pftLastWriteTime = NULL; UINT32 FileAttributes; UINT32 FileNameLength; - HANDLE hFd; LARGE_INTEGER liSize; + UINT8 delete_pending; + UINT8 ReplaceIfExists; + DWORD attr; if (!file || !input) return FALSE; @@ -595,15 +573,12 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN Stream_Read_UINT64(input, liChangeTime.QuadPart); Stream_Read_UINT32(input, FileAttributes); - if (!PathFileExistsA(file->fullpath)) + if (!PathFileExistsW(file->fullpath)) return FALSE; - hFd = CreateFileA(file->fullpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - - if (hFd == INVALID_HANDLE_VALUE) + if (file->file_handle == INVALID_HANDLE_VALUE) { - WLog_ERR(TAG, "Unable to create file %s", file->fullpath); + WLog_ERR(TAG, "Unable to set file time %s (%"PRId32")", file->fullpath, GetLastError()); return FALSE; } @@ -635,14 +610,15 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN pftLastWriteTime = &ftLastWriteTime; } - if (!SetFileTime(hFd, pftCreationTime, pftLastAccessTime, pftLastWriteTime)) + DEBUG_WSTR("SetFileTime %s", file->fullpath); + + if (!SetFileTime(file->file_handle, pftCreationTime, pftLastAccessTime, pftLastWriteTime)) { - WLog_ERR(TAG, "Unable to set file time on %s", file->fullpath); - CloseHandle(hFd); + WLog_ERR(TAG, "Unable to set file time to %s", file->fullpath); return FALSE; } - CloseHandle(hFd); + SetFileAttributesW(file->fullpath, FileAttributes); break; case FileEndOfFileInformation: @@ -651,79 +627,88 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN case FileAllocationInformation: /* http://msdn.microsoft.com/en-us/library/cc232076.aspx */ Stream_Read_INT64(input, size); - hFd = CreateFileA(file->fullpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, NULL); - if (hFd == INVALID_HANDLE_VALUE) + if (file->file_handle == INVALID_HANDLE_VALUE) { - WLog_ERR(TAG, "Unable to truncate %s to %"PRId64"", file->fullpath, size); + WLog_ERR(TAG, "Unable to truncate %s to %"PRId64" (%"PRId32")", file->fullpath, size, GetLastError()); return FALSE; } - liSize.QuadPart = size; + liSize.QuadPart = size & 0xFFFFFFFF; - if (SetFilePointer(hFd, liSize.LowPart, &liSize.HighPart, FILE_BEGIN) == 0) + if (SetFilePointer(file->file_handle, liSize.LowPart, &liSize.HighPart, + FILE_BEGIN) == INVALID_SET_FILE_POINTER) { - WLog_ERR(TAG, "Unable to truncate %s to %"PRId64"", file->fullpath, size); - CloseHandle(hFd); + WLog_ERR(TAG, "Unable to truncate %s to %d (%"PRId32")", file->fullpath, size, GetLastError()); + return FALSE; + } + + DEBUG_WSTR("Truncate %s", file->fullpath); + + if (SetEndOfFile(file->file_handle) == 0) + { + WLog_ERR(TAG, "Unable to truncate %s to %d (%"PRId32")", file->fullpath, size, GetLastError()); return FALSE; } - CloseHandle(hFd); break; case FileDispositionInformation: /* http://msdn.microsoft.com/en-us/library/cc232098.aspx */ /* http://msdn.microsoft.com/en-us/library/cc241371.aspx */ - if (file->is_dir && !dir_empty(file->fullpath)) - break; + if (file->is_dir && !PathIsDirectoryEmptyW(file->fullpath)) + break; /* TODO: SetLastError ??? */ if (Length) - Stream_Read_UINT8(input, file->delete_pending); + Stream_Read_UINT8(input, delete_pending); else - file->delete_pending = 1; + delete_pending = 1; + if (delete_pending) + { + DEBUG_WSTR("SetDeletePending %s", file->fullpath); + attr = GetFileAttributesW(file->fullpath); + + if (attr & FILE_ATTRIBUTE_READONLY) + { + SetLastError(ERROR_ACCESS_DENIED); + return FALSE; + } + } + + file->delete_pending = delete_pending; break; case FileRenameInformation: /* http://msdn.microsoft.com/en-us/library/cc232085.aspx */ - Stream_Seek_UINT8(input); /* ReplaceIfExists */ + Stream_Read_UINT8(input, ReplaceIfExists); Stream_Seek_UINT8(input); /* RootDirectory */ Stream_Read_UINT32(input, FileNameLength); - status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(input), - FileNameLength / 2, &s, 0, NULL, NULL); - - if (status < 1) - if (!(s = (char*) calloc(1, 1))) - { - WLog_ERR(TAG, "calloc failed!"); - return FALSE; - } - - fullpath = drive_file_combine_fullpath(file->basepath, s); + fullpath = drive_file_combine_fullpath(file->basepath, (WCHAR*)Stream_Pointer(input), + FileNameLength); if (!fullpath) { WLog_ERR(TAG, "drive_file_combine_fullpath failed!"); - free(s); return FALSE; } - free(s); #ifdef _WIN32 - if (file->fd) - close(file->fd); + if (file->file_handle != INVALID_HANDLE_VALUE) + { + CloseHandle(file->file_handle); + file->file_handle = INVALID_HANDLE_VALUE; + } #endif + DEBUG_WSTR("MoveFileExW %s", file->fullpath); - if (rename(file->fullpath, fullpath) == 0) + if (MoveFileExW(file->fullpath, fullpath, + MOVEFILE_COPY_ALLOWED | (ReplaceIfExists ? MOVEFILE_REPLACE_EXISTING : 0))) { drive_file_set_fullpath(file, fullpath); -#ifdef _WIN32 - file->fd = OPEN(fullpath, O_RDWR | O_BINARY); -#endif } else { @@ -731,6 +716,9 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN return FALSE; } +#ifdef _WIN32 + drive_file_init(file); +#endif break; default: @@ -741,86 +729,32 @@ BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UIN } BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery, - const char* path, wStream* output) + const WCHAR* path, UINT32 PathLength, wStream* output) { int length; - BOOL ret; WCHAR* ent_path; - struct STAT st; - struct dirent* ent; if (!file || !path || !output) return FALSE; - if (!file->dir) - { - Stream_Write_UINT32(output, 0); /* Length */ - Stream_Write_UINT8(output, 0); /* Padding */ - return FALSE; - } - if (InitialQuery != 0) { - rewinddir(file->dir); - free(file->pattern); + /* release search handle */ + if (file->find_handle != INVALID_HANDLE_VALUE) + FindClose(file->find_handle); - if (path[0]) - { - if (!(file->pattern = _strdup(strrchr(path, '\\') + 1))) - { - WLog_ERR(TAG, "_strdup failed!"); - return FALSE; - } - } - else - file->pattern = NULL; + ent_path = drive_file_combine_fullpath(file->basepath, path, PathLength); + /* open new search handle and retrieve the first entry */ + file->find_handle = FindFirstFileW(ent_path, &file->find_data); + free(ent_path); + + if (file->find_handle == INVALID_HANDLE_VALUE) + goto out_fail; } + else if (!FindNextFileW(file->find_handle, &file->find_data)) + goto out_fail; - if (file->pattern) - { - do - { - ent = readdir(file->dir); - - if (ent == NULL) - continue; - - if (FilePatternMatchA(ent->d_name, file->pattern)) - break; - } - while (ent); - } - else - { - ent = readdir(file->dir); - } - - if (!ent) - { - Stream_Write_UINT32(output, 0); /* Length */ - Stream_Write_UINT8(output, 0); /* Padding */ - return FALSE; - } - - memset(&st, 0, sizeof(struct STAT)); - ent_path = (WCHAR*) malloc(strlen(file->fullpath) + strlen(ent->d_name) + 2); - - if (!ent_path) - { - WLog_ERR(TAG, "malloc failed!"); - return FALSE; - } - - sprintf((char*) ent_path, "%s/%s", file->fullpath, ent->d_name); - - if (STAT((char*) ent_path, &st) != 0) - { - } - - free(ent_path); - ent_path = NULL; - length = ConvertToUnicode(sys_code_page, 0, ent->d_name, -1, &ent_path, 0) * 2; - ret = TRUE; + length = _wcslen(file->find_data.cFileName) * 2; switch (FsInformationClass) { @@ -833,15 +767,21 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, 64 + length); /* Length */ Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ - Stream_Write_UINT64(output, st.st_size); /* EndOfFile */ - Stream_Write_UINT64(output, st.st_size); /* AllocationSize */ - Stream_Write_UINT32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */ + Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */ + Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */ + Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */ + Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */ + Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */ + Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */ Stream_Write_UINT32(output, length); /* FileNameLength */ - Stream_Write(output, ent_path, length); + Stream_Write(output, file->find_data.cFileName, length); break; case FileFullDirectoryInformation: @@ -853,16 +793,22 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, 68 + length); /* Length */ Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ - Stream_Write_UINT64(output, st.st_size); /* EndOfFile */ - Stream_Write_UINT64(output, st.st_size); /* AllocationSize */ - Stream_Write_UINT32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */ + Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */ + Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */ + Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */ + Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */ + Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */ + Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */ Stream_Write_UINT32(output, length); /* FileNameLength */ Stream_Write_UINT32(output, 0); /* EaSize */ - Stream_Write(output, ent_path, length); + Stream_Write(output, file->find_data.cFileName, length); break; case FileBothDirectoryInformation: @@ -874,19 +820,25 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, 93 + length); /* Length */ Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* CreationTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_atime)); /* LastAccessTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_mtime)); /* LastWriteTime */ - Stream_Write_UINT64(output, FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* ChangeTime */ - Stream_Write_UINT64(output, st.st_size); /* EndOfFile */ - Stream_Write_UINT64(output, st.st_size); /* AllocationSize */ - Stream_Write_UINT32(output, FILE_ATTR_SYSTEM_TO_RDP(file, st)); /* FileAttributes */ + Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwLowDateTime); /* CreationTime */ + Stream_Write_UINT32(output, file->find_data.ftCreationTime.dwHighDateTime); /* CreationTime */ + Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwLowDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, file->find_data.ftLastAccessTime.dwHighDateTime); /* LastAccessTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* LastWriteTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwLowDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, file->find_data.ftLastWriteTime.dwHighDateTime); /* ChangeTime */ + Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* EndOfFile */ + Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* EndOfFile */ + Stream_Write_UINT32(output, file->find_data.nFileSizeLow); /* AllocationSize */ + Stream_Write_UINT32(output, file->find_data.nFileSizeHigh); /* AllocationSize */ + Stream_Write_UINT32(output, file->find_data.dwFileAttributes); /* FileAttributes */ Stream_Write_UINT32(output, length); /* FileNameLength */ Stream_Write_UINT32(output, 0); /* EaSize */ Stream_Write_UINT8(output, 0); /* ShortNameLength */ /* Reserved(1), MUST NOT be added! */ Stream_Zero(output, 24); /* ShortName */ - Stream_Write(output, ent_path, length); + Stream_Write(output, file->find_data.cFileName, length); break; case FileNamesInformation: @@ -899,21 +851,16 @@ BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYT Stream_Write_UINT32(output, 0); /* NextEntryOffset */ Stream_Write_UINT32(output, 0); /* FileIndex */ Stream_Write_UINT32(output, length); /* FileNameLength */ - Stream_Write(output, ent_path, length); + Stream_Write(output, file->find_data.cFileName, length); break; default: /* Unhandled FsInformationClass */ - Stream_Write_UINT32(output, 0); /* Length */ - Stream_Write_UINT8(output, 0); /* Padding */ - ret = FALSE; - break; + goto out_fail; } - free(ent_path); - return ret; + return TRUE; out_fail: - free(ent_path); Stream_Write_UINT32(output, 0); /* Length */ Stream_Write_UINT8(output, 0); /* Padding */ return FALSE; diff --git a/channels/drive/client/drive_file.h b/channels/drive/client/drive_file.h index 092e7d338..1cdfb3353 100644 --- a/channels/drive/client/drive_file.h +++ b/channels/drive/client/drive_file.h @@ -30,69 +30,6 @@ #include #include -#ifdef _WIN32 -#include -#include -#include "dirent.h" -#include "statvfs.h" -#else -#include -#ifdef ANDROID -#include -#else -#include -#endif -#endif - -#ifdef _WIN32 -#define STAT __stat64 -#define OPEN _open -#define close _close -#define read _read -#define write _write -#define LSEEK _lseeki64 -#define FSTAT _fstat64 -#define STATVFS statvfs -#define mkdir(a,b) _mkdir(a) -#define rmdir _rmdir -#define unlink(a) _unlink(a) -#define ftruncate(a,b) _chsize(a,b) - -typedef UINT32 ssize_t; -typedef UINT32 mode_t; - -#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) -#define STAT stat -#define OPEN open -#define LSEEK lseek -#define FSTAT fstat -#define STATVFS statvfs -#define O_LARGEFILE 0 -#elif defined(ANDROID) -#define STAT stat -#define OPEN open -#define LSEEK lseek -#define FSTAT fstat -#define STATVFS statfs -#else -#define STAT stat64 -#define OPEN open64 -#define LSEEK lseek64 -#define FSTAT fstat64 -#define STATVFS statvfs64 -#endif - -#define EPOCH_DIFF 11644473600LL - -#define FILE_TIME_SYSTEM_TO_RDP(_t) \ - (((UINT64)(_t) + EPOCH_DIFF) * 10000000LL) - -#define FILE_ATTR_SYSTEM_TO_RDP(_f, _st) ( \ - (S_ISDIR(_st.st_mode) ? FILE_ATTRIBUTE_DIRECTORY : 0) | \ - (_f->filename[0] == '.' ? FILE_ATTRIBUTE_HIDDEN : 0) | \ - (_f->delete_pending ? FILE_ATTRIBUTE_TEMPORARY : 0) | \ - (st.st_mode & S_IWUSR ? 0 : FILE_ATTRIBUTE_READONLY)) - #define TAG CHANNELS_TAG("drive.client") typedef struct _DRIVE_FILE DRIVE_FILE; @@ -101,28 +38,34 @@ struct _DRIVE_FILE { UINT32 id; BOOL is_dir; - int fd; - int err; - DIR* dir; - char* basepath; - char* fullpath; - char* filename; - char* pattern; + HANDLE file_handle; + HANDLE find_handle; + WIN32_FIND_DATAW find_data; + WCHAR* basepath; + WCHAR* fullpath; + WCHAR* filename; BOOL delete_pending; + UINT32 FileAttributes; + UINT32 SharedAccess; + UINT32 DesiredAccess; + UINT32 CreateDisposition; + UINT32 CreateOptions; }; -DRIVE_FILE* drive_file_new(const char* base_path, const char* path, UINT32 id, - UINT32 DesiredAccess, UINT32 CreateDisposition, UINT32 CreateOptions); -void drive_file_free(DRIVE_FILE* file); +DRIVE_FILE* drive_file_new(const WCHAR* base_path, const WCHAR* path, UINT32 PathLength, UINT32 id, + UINT32 DesiredAccess, UINT32 CreateDisposition, + UINT32 CreateOptions, UINT32 FileAttributes, UINT32 SharedAccess); +BOOL drive_file_free(DRIVE_FILE* file); +BOOL drive_file_open(DRIVE_FILE* file); BOOL drive_file_seek(DRIVE_FILE* file, UINT64 Offset); BOOL drive_file_read(DRIVE_FILE* file, BYTE* buffer, UINT32* Length); BOOL drive_file_write(DRIVE_FILE* file, BYTE* buffer, UINT32 Length); BOOL drive_file_query_information(DRIVE_FILE* file, UINT32 FsInformationClass, wStream* output); -BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, wStream* input); +BOOL drive_file_set_information(DRIVE_FILE* file, UINT32 FsInformationClass, UINT32 Length, + wStream* input); BOOL drive_file_query_directory(DRIVE_FILE* file, UINT32 FsInformationClass, BYTE InitialQuery, - const char* path, wStream* output); -int dir_empty(const char *path); + const WCHAR* path, UINT32 PathLength, wStream* output); extern UINT sys_code_page; diff --git a/channels/drive/client/drive_main.c b/channels/drive/client/drive_main.c index 24c483f67..827244698 100644 --- a/channels/drive/client/drive_main.c +++ b/channels/drive/client/drive_main.c @@ -6,6 +6,7 @@ * Copyright 2010-2012 Marc-Andre Moreau * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 DI (FH) Martin Haimberger + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -39,6 +40,7 @@ #include #include +#include #include #include #include @@ -46,6 +48,7 @@ #include #include #include +#include #include @@ -57,7 +60,8 @@ struct _DRIVE_DEVICE { DEVICE device; - char* path; + WCHAR* path; + UINT32 PathLength; wListDictionary* files; HANDLE thread; @@ -68,37 +72,59 @@ struct _DRIVE_DEVICE rdpContext* rdpcontext; }; -static UINT32 drive_map_posix_err(int fs_errno) +static DWORD drive_map_windows_err(DWORD fs_errno) { - UINT32 rc; + DWORD rc; /* try to return NTSTATUS version of error code */ switch (fs_errno) { - case EPERM: - case EACCES: + case STATUS_SUCCESS: + rc = STATUS_SUCCESS; + break; + + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: rc = STATUS_ACCESS_DENIED; break; - case ENOENT: + case ERROR_FILE_NOT_FOUND: rc = STATUS_NO_SUCH_FILE; break; - case EBUSY: + case ERROR_BUSY_DRIVE: rc = STATUS_DEVICE_BUSY; break; - case EEXIST: + case ERROR_FILE_EXISTS: + case ERROR_ALREADY_EXISTS: rc = STATUS_OBJECT_NAME_COLLISION; break; - case EISDIR: - rc = STATUS_FILE_IS_A_DIRECTORY; + case ERROR_INVALID_NAME: + rc = STATUS_NO_SUCH_FILE; + break; + + case ERROR_INVALID_HANDLE: + rc = STATUS_INVALID_HANDLE; + break; + + case ERROR_NO_MORE_FILES: + rc = STATUS_NO_MORE_FILES; + break; + + case ERROR_DIRECTORY: + rc = STATUS_NOT_A_DIRECTORY; + break; + + case ERROR_PATH_NOT_FOUND: + rc = STATUS_OBJECT_PATH_NOT_FOUND; break; default: rc = STATUS_UNSUCCESSFUL; + WLog_ERR(TAG, "Error code not found: %"PRId32"", fs_errno); break; } @@ -120,54 +146,35 @@ static DRIVE_FILE* drive_get_file_by_id(DRIVE_DEVICE* drive, UINT32 id) */ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp) { - int status; void* key; UINT32 FileId; DRIVE_FILE* file; BYTE Information; + UINT32 FileAttributes; + UINT32 SharedAccess; UINT32 DesiredAccess; UINT32 CreateDisposition; UINT32 CreateOptions; UINT32 PathLength; - char* path = NULL; + const WCHAR* path; Stream_Read_UINT32(irp->input, DesiredAccess); - Stream_Seek(irp->input, - 16); /* AllocationSize(8), FileAttributes(4), SharedAccess(4) */ + Stream_Seek(irp->input, 8); /* AllocationSize(8) */ + Stream_Read_UINT32(irp->input, FileAttributes); + Stream_Read_UINT32(irp->input, SharedAccess); Stream_Read_UINT32(irp->input, CreateDisposition); Stream_Read_UINT32(irp->input, CreateOptions); Stream_Read_UINT32(irp->input, PathLength); - status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input), - PathLength / 2, &path, 0, NULL, NULL); - - if (status < 1) - { - path = (char*) calloc(1, 1); - - if (!path) - { - WLog_ERR(TAG, "calloc failed!"); - return CHANNEL_RC_NO_MEMORY; - } - } - + path = (WCHAR*) Stream_Pointer(irp->input); FileId = irp->devman->id_sequence++; - file = drive_file_new(drive->path, path, FileId, - DesiredAccess, CreateDisposition, CreateOptions); + file = drive_file_new(drive->path, path, PathLength, FileId, DesiredAccess, CreateDisposition, + CreateOptions, FileAttributes, SharedAccess); if (!file) { - irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = drive_map_windows_err(GetLastError()); FileId = 0; Information = 0; } - else if (file->err) - { - FileId = 0; - Information = 0; - /* map errno to windows result */ - irp->IoStatus = drive_map_posix_err(file->err); - drive_file_free(file); - } else { key = (void*)(size_t) file->id; @@ -175,7 +182,6 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp) if (!ListDictionary_Add(drive->files, key, file)) { WLog_ERR(TAG, "ListDictionary_Add failed!"); - free(path); return ERROR_INTERNAL_ERROR; } @@ -204,7 +210,6 @@ static UINT drive_process_irp_create(DRIVE_DEVICE* drive, IRP* irp) Stream_Write_UINT32(irp->output, FileId); Stream_Write_UINT8(irp->output, Information); - free(path); return irp->Complete(irp); } @@ -227,7 +232,11 @@ static UINT drive_process_irp_close(DRIVE_DEVICE* drive, IRP* irp) else { ListDictionary_Remove(drive->files, key); - drive_file_free(file); + + if (drive_file_free(file)) + irp->IoStatus = STATUS_SUCCESS; + else + irp->IoStatus = drive_map_windows_err(GetLastError()); } Stream_Zero(irp->output, 5); /* Padding(5) */ @@ -256,7 +265,7 @@ static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp) } else if (!drive_file_seek(file, Offset)) { - irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = drive_map_windows_err(GetLastError()); Length = 0; } else @@ -271,7 +280,7 @@ static UINT drive_process_irp_read(DRIVE_DEVICE* drive, IRP* irp) if (!drive_file_read(file, buffer, &Length)) { - irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = drive_map_windows_err(GetLastError()); free(buffer); buffer = NULL; Length = 0; @@ -317,12 +326,12 @@ static UINT drive_process_irp_write(DRIVE_DEVICE* drive, IRP* irp) } else if (!drive_file_seek(file, Offset)) { - irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = drive_map_windows_err(GetLastError()); Length = 0; } else if (!drive_file_write(file, Stream_Pointer(irp->input), Length)) { - irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = drive_map_windows_err(GetLastError()); Length = 0; } @@ -349,7 +358,7 @@ static UINT drive_process_irp_query_information(DRIVE_DEVICE* drive, IRP* irp) } else if (!drive_file_query_information(file, FsInformationClass, irp->output)) { - irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = drive_map_windows_err(GetLastError()); } return irp->Complete(irp); @@ -377,16 +386,17 @@ static UINT drive_process_irp_set_information(DRIVE_DEVICE* drive, IRP* irp) else if (!drive_file_set_information(file, FsInformationClass, Length, irp->input)) { - irp->IoStatus = STATUS_UNSUCCESSFUL; + irp->IoStatus = drive_map_windows_err(GetLastError()); } - if (file && file->is_dir && !dir_empty(file->fullpath)) + if (file && file->is_dir && !PathIsDirectoryEmptyW(file->fullpath)) irp->IoStatus = STATUS_DIRECTORY_NOT_EMPTY; Stream_Write_UINT32(irp->output, Length); return irp->Complete(irp); } + /** * Function description * @@ -397,21 +407,29 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, { UINT32 FsInformationClass; wStream* output = irp->output; - struct STATVFS svfst; - struct STAT st; char* volumeLabel = {"FREERDP"}; char* diskType = {"FAT32"}; WCHAR* outStr = NULL; int length; + DWORD lpSectorsPerCluster; + DWORD lpBytesPerSector; + DWORD lpNumberOfFreeClusters; + DWORD lpTotalNumberOfClusters; + WIN32_FILE_ATTRIBUTE_DATA wfad; Stream_Read_UINT32(irp->input, FsInformationClass); - STATVFS(drive->path, &svfst); - STAT(drive->path, &st); + GetDiskFreeSpaceW(drive->path, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters, + &lpTotalNumberOfClusters); switch (FsInformationClass) { case FileFsVolumeInformation: /* http://msdn.microsoft.com/en-us/library/cc232108.aspx */ - length = ConvertToUnicode(sys_code_page, 0, volumeLabel, -1, &outStr, 0) * 2; + if ((length = ConvertToUnicode(sys_code_page, 0, volumeLabel, -1, &outStr, 0) * 2) <= 0) + { + WLog_ERR(TAG, "ConvertToUnicode failed!"); + return CHANNEL_RC_NO_MEMORY; + } + Stream_Write_UINT32(output, 17 + length); /* Length */ if (!Stream_EnsureRemainingCapacity(output, 17 + length)) @@ -421,13 +439,10 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, return CHANNEL_RC_NO_MEMORY; } - Stream_Write_UINT64(output, - FILE_TIME_SYSTEM_TO_RDP(st.st_ctime)); /* VolumeCreationTime */ -#ifdef ANDROID - Stream_Write_UINT32(output, svfst.f_fsid.__val[0]); /* VolumeSerialNumber */ -#else - Stream_Write_UINT32(output, svfst.f_fsid); /* VolumeSerialNumber */ -#endif + GetFileAttributesExW(drive->path, GetFileExInfoStandard, &wfad); + Stream_Write_UINT32(output, wfad.ftCreationTime.dwLowDateTime); /* VolumeCreationTime */ + Stream_Write_UINT32(output, wfad.ftCreationTime.dwHighDateTime); /* VolumeCreationTime */ + Stream_Write_UINT32(output, lpNumberOfFreeClusters & 0xffff); /* VolumeSerialNumber */ Stream_Write_UINT32(output, length); /* VolumeLabelLength */ Stream_Write_UINT8(output, 0); /* SupportsObjects */ /* Reserved(1), MUST NOT be added! */ @@ -445,15 +460,20 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, return CHANNEL_RC_NO_MEMORY; } - Stream_Write_UINT64(output, svfst.f_blocks); /* TotalAllocationUnits */ - Stream_Write_UINT64(output, svfst.f_bavail); /* AvailableAllocationUnits */ - Stream_Write_UINT32(output, 1); /* SectorsPerAllocationUnit */ - Stream_Write_UINT32(output, svfst.f_bsize); /* BytesPerSector */ + Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */ + Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */ + Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */ + Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */ break; case FileFsAttributeInformation: /* http://msdn.microsoft.com/en-us/library/cc232101.aspx */ - length = ConvertToUnicode(sys_code_page, 0, diskType, -1, &outStr, 0) * 2; + if ((length = ConvertToUnicode(sys_code_page, 0, diskType, -1, &outStr, 0) * 2) <= 0) + { + WLog_ERR(TAG, "ConvertToUnicode failed!"); + return CHANNEL_RC_NO_MEMORY; + } + Stream_Write_UINT32(output, 12 + length); /* Length */ if (!Stream_EnsureRemainingCapacity(output, 12 + length)) @@ -466,12 +486,7 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, FILE_CASE_SENSITIVE_SEARCH | FILE_CASE_PRESERVED_NAMES | FILE_UNICODE_ON_DISK); /* FileSystemAttributes */ -#ifdef ANDROID - Stream_Write_UINT32(output, 255); /* MaximumComponentNameLength */ -#else - Stream_Write_UINT32(output, - svfst.f_namemax/*510*/); /* MaximumComponentNameLength */ -#endif + Stream_Write_UINT32(output, MAX_PATH); /* MaximumComponentNameLength */ Stream_Write_UINT32(output, length); /* FileSystemNameLength */ Stream_Write(output, outStr, length); /* FileSystemName (Unicode) */ free(outStr); @@ -487,12 +502,11 @@ static UINT drive_process_irp_query_volume_information(DRIVE_DEVICE* drive, return CHANNEL_RC_NO_MEMORY; } - Stream_Write_UINT64(output, svfst.f_blocks); /* TotalAllocationUnits */ - Stream_Write_UINT64(output, - svfst.f_bavail); /* CallerAvailableAllocationUnits */ - Stream_Write_UINT64(output, svfst.f_bfree); /* AvailableAllocationUnits */ - Stream_Write_UINT32(output, 1); /* SectorsPerAllocationUnit */ - Stream_Write_UINT32(output, svfst.f_bsize); /* BytesPerSector */ + Stream_Write_UINT64(output, lpTotalNumberOfClusters); /* TotalAllocationUnits */ + Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* CallerAvailableAllocationUnits */ + Stream_Write_UINT64(output, lpNumberOfFreeClusters); /* AvailableAllocationUnits */ + Stream_Write_UINT32(output, lpSectorsPerCluster); /* SectorsPerAllocationUnit */ + Stream_Write_UINT32(output, lpBytesPerSector); /* BytesPerSector */ break; case FileFsDeviceInformation: @@ -541,8 +555,7 @@ static UINT drive_process_irp_silent_ignore(DRIVE_DEVICE* drive, IRP* irp) */ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp) { - char* path = NULL; - int status; + const WCHAR* path; DRIVE_FILE* file; BYTE InitialQuery; UINT32 PathLength; @@ -551,16 +564,8 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp) Stream_Read_UINT8(irp->input, InitialQuery); Stream_Read_UINT32(irp->input, PathLength); Stream_Seek(irp->input, 23); /* Padding */ - status = ConvertFromUnicode(CP_UTF8, 0, (WCHAR*) Stream_Pointer(irp->input), - PathLength / 2, &path, 0, NULL, NULL); - - if (status < 1) - if (!(path = (char*) calloc(1, 1))) - { - WLog_ERR(TAG, "calloc failed!"); - return CHANNEL_RC_NO_MEMORY; - } + path = (WCHAR*) Stream_Pointer(irp->input); file = drive_get_file_by_id(drive, irp->FileId); if (file == NULL) @@ -568,13 +573,12 @@ static UINT drive_process_irp_query_directory(DRIVE_DEVICE* drive, IRP* irp) irp->IoStatus = STATUS_UNSUCCESSFUL; Stream_Write_UINT32(irp->output, 0); /* Length */ } - else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, - path, irp->output)) + else if (!drive_file_query_directory(file, FsInformationClass, InitialQuery, path, PathLength, + irp->output)) { - irp->IoStatus = STATUS_NO_MORE_FILES; + irp->IoStatus = drive_map_windows_err(GetLastError()); } - free(path); return irp->Complete(irp); } @@ -714,8 +718,7 @@ static void* drive_thread_func(void* arg) } if (error && drive->rdpcontext) - setChannelError(drive->rdpcontext, error, - "drive_thread_func reported an error"); + setChannelError(drive->rdpcontext, error, "drive_thread_func reported an error"); ExitThread((DWORD)error); return NULL; @@ -820,7 +823,13 @@ UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, for (i = 0; i <= length; i++) Stream_Write_UINT8(drive->device.data, name[i] < 0 ? '_' : name[i]); - drive->path = path; + if (ConvertToUnicode(sys_code_page, 0, path, -1, &drive->path, 0) <= 0) + { + WLog_ERR(TAG, "ConvertToUnicode failed!"); + error = CHANNEL_RC_NO_MEMORY; + goto out_error; + } + drive->files = ListDictionary_New(TRUE); if (!drive->files) @@ -830,8 +839,7 @@ UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, goto out_error; } - ListDictionary_ValueObject(drive->files)->fnObjectFree = - (OBJECT_FREE_FN) drive_file_free; + ListDictionary_ValueObject(drive->files)->fnObjectFree = (OBJECT_FREE_FN) drive_file_free; drive->IrpQueue = MessageQueue_New(NULL); if (!drive->IrpQueue) @@ -848,8 +856,8 @@ UINT drive_register_drive_path(PDEVICE_SERVICE_ENTRY_POINTS pEntryPoints, goto out_error; } - if (!(drive->thread = CreateThread(NULL, 0, - (LPTHREAD_START_ROUTINE) drive_thread_func, drive, CREATE_SUSPENDED, NULL))) + if (!(drive->thread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) drive_thread_func, drive, + CREATE_SUSPENDED, NULL))) { WLog_ERR(TAG, "CreateThread failed!"); goto out_error; diff --git a/channels/drive/client/statvfs.c b/channels/drive/client/statvfs.c deleted file mode 100644 index e92a97557..000000000 --- a/channels/drive/client/statvfs.c +++ /dev/null @@ -1,60 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * statvfs emulation for Windows - * - * Copyright 2012 Gerald Richter - * Copyright 2016 Inuvika Inc. - * Copyright 2016 David PHAM-VAN - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include - -#include -#include - -#include "statvfs.h" - -int statvfs(const char *path, struct statvfs *buf) -{ - BOOL res; - int len; - LPWSTR unicodestr = NULL; - DWORD lpSectorsPerCluster; - DWORD lpBytesPerSector; - DWORD lpNumberOfFreeClusters; - DWORD lpTotalNumberOfClusters; - - len = ConvertToUnicode(CP_ACP, 0, path, -1, &unicodestr, 0); - if (len <= 0) - return -1; - - res = GetDiskFreeSpaceW(unicodestr, &lpSectorsPerCluster, &lpBytesPerSector, &lpNumberOfFreeClusters, &lpTotalNumberOfClusters); - free(unicodestr); - - buf->f_bsize = lpBytesPerSector; /* file system block size */ - buf->f_frsize = 0; /* fragment size */ - buf->f_blocks = lpTotalNumberOfClusters; /* size of fs in f_frsize units */ - buf->f_bfree = lpNumberOfFreeClusters; /* # free blocks */ - buf->f_bavail = lpNumberOfFreeClusters; /* # free blocks for unprivileged users */ - buf->f_files = 0; /* # inodes */ - buf->f_ffree = 0; /* # free inodes */ - buf->f_favail = 0; /* # free inodes for unprivileged users */ - buf->f_fsid = lpNumberOfFreeClusters & 0xffff; /* file system ID */ - buf->f_flag = 0; /* mount flags */ - buf->f_namemax = 250; /* maximum filename length */ - - return res; -} diff --git a/channels/drive/client/statvfs.h b/channels/drive/client/statvfs.h deleted file mode 100644 index 6912c29a4..000000000 --- a/channels/drive/client/statvfs.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * FreeRDP: A Remote Desktop Protocol Implementation - * statvfs emulation for windows - * - * Copyright 2012 Gerald Richter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef RDPDR_DISK_STATVFS_H -#define RDPDR_DISK_STATVFS_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef unsigned long long fsblkcnt_t; -typedef unsigned long long fsfilcnt_t; - -struct statvfs { - unsigned long f_bsize; /* file system block size */ - unsigned long f_frsize; /* fragment size */ - fsblkcnt_t f_blocks; /* size of fs in f_frsize units */ - fsblkcnt_t f_bfree; /* # free blocks */ - fsblkcnt_t f_bavail; /* # free blocks for unprivileged users */ - fsfilcnt_t f_files; /* # inodes */ - fsfilcnt_t f_ffree; /* # free inodes */ - fsfilcnt_t f_favail; /* # free inodes for unprivileged users */ - unsigned long f_fsid; /* file system ID */ - unsigned long f_flag; /* mount flags */ - unsigned long f_namemax; /* maximum filename length */ -}; - -int statvfs(const char *path, struct statvfs *buf); - -#ifdef __cplusplus -} -#endif - -#endif /* RDPDR_DISK_STATVFS_H */ diff --git a/ci/cmake-preloads/config-linux-all.txt b/ci/cmake-preloads/config-linux-all.txt index ebad39185..dd3221b5b 100644 --- a/ci/cmake-preloads/config-linux-all.txt +++ b/ci/cmake-preloads/config-linux-all.txt @@ -30,6 +30,7 @@ set (WITH_DEBUG_RAIL OFF CACHE BOOL "enable debug") set (WITH_DEBUG_RDP OFF CACHE BOOL "enable debug") set (WITH_DEBUG_RDPEI OFF CACHE BOOL "enable debug") set (WITH_DEBUG_REDIR OFF CACHE BOOL "enable debug") +set (WITH_DEBUG_RDPDR OFF CACHE BOOL "enable debug") set (WITH_DEBUG_RFX OFF CACHE BOOL "enable debug") set (WITH_DEBUG_SCARD OFF CACHE BOOL "enable debug") set (WITH_DEBUG_SND OFF CACHE BOOL "enable debug") diff --git a/cmake/ConfigOptions.cmake b/cmake/ConfigOptions.cmake index 905863eed..67b298e13 100644 --- a/cmake/ConfigOptions.cmake +++ b/cmake/ConfigOptions.cmake @@ -112,6 +112,7 @@ option(WITH_DEBUG_RAIL "Print RemoteApp debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_RDP "Print RDP debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_RDPEI "Print input virtual channel debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_REDIR "Redirection debug messages" ${DEFAULT_DEBUG_OPTION}) +option(WITH_DEBUG_RDPDR "Rdpdr debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_RFX "Print RemoteFX debug messages." ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_SCARD "Print smartcard debug messages" ${DEFAULT_DEBUG_OPTION}) option(WITH_DEBUG_SND "Print rdpsnd debug messages" ${DEFAULT_DEBUG_OPTION}) diff --git a/config.h.in b/config.h.in index 444171097..0c8ab218e 100644 --- a/config.h.in +++ b/config.h.in @@ -71,6 +71,7 @@ #cmakedefine WITH_DEBUG_RAIL #cmakedefine WITH_DEBUG_RDP #cmakedefine WITH_DEBUG_REDIR +#cmakedefine WITH_DEBUG_RDPDR #cmakedefine WITH_DEBUG_RFX #cmakedefine WITH_DEBUG_SCARD #cmakedefine WITH_DEBUG_SND diff --git a/winpr/include/winpr/file.h b/winpr/include/winpr/file.h index 7b050ab34..f2610e787 100644 --- a/winpr/include/winpr/file.h +++ b/winpr/include/winpr/file.h @@ -3,6 +3,7 @@ * File Functions * * Copyright 2012 Marc-Andre Moreau + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -172,6 +173,13 @@ #define LOCKFILE_FAIL_IMMEDIATELY 1 #define LOCKFILE_EXCLUSIVE_LOCK 2 +#define MOVEFILE_REPLACE_EXISTING 0x1 +#define MOVEFILE_COPY_ALLOWED 0x2 +#define MOVEFILE_DELAY_UNTIL_REBOOT 0x4 +#define MOVEFILE_WRITE_THROUGH 0x8 +#define MOVEFILE_CREATE_HARDLINK 0x10 +#define MOVEFILE_FAIL_IF_NOT_TRACKABLE 0x20 + typedef union _FILE_SEGMENT_ELEMENT { PVOID64 Buffer; @@ -266,6 +274,34 @@ WINPR_API BOOL WriteFileGather(HANDLE hFile, FILE_SEGMENT_ELEMENT aSegmentArray[ WINPR_API BOOL FlushFileBuffers(HANDLE hFile); +typedef struct _WIN32_FILE_ATTRIBUTE_DATA +{ + DWORD dwFileAttributes; + FILETIME ftCreationTime; + FILETIME ftLastAccessTime; + FILETIME ftLastWriteTime; + DWORD nFileSizeHigh; + DWORD nFileSizeLow; +} WIN32_FILE_ATTRIBUTE_DATA, *LPWIN32_FILE_ATTRIBUTE_DATA; + +typedef enum _GET_FILEEX_INFO_LEVELS +{ + GetFileExInfoStandard, + GetFileExMaxInfoLevel +} GET_FILEEX_INFO_LEVELS; + +WINPR_API BOOL GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation); + +WINPR_API DWORD GetFileAttributesA(LPCSTR lpFileName); + +WINPR_API BOOL GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, LPVOID lpFileInformation); + +WINPR_API DWORD GetFileAttributesW(LPCWSTR lpFileName); + +WINPR_API BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes); + +WINPR_API BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes); + WINPR_API BOOL SetEndOfFile(HANDLE hFile); WINPR_API DWORD WINAPI GetFileSize(HANDLE hFile, LPDWORD lpFileSizeHigh); @@ -314,6 +350,20 @@ WINPR_API HANDLE GetStdHandle(DWORD nStdHandle); WINPR_API BOOL SetStdHandle(DWORD nStdHandle, HANDLE hHandle); WINPR_API BOOL SetStdHandleEx(DWORD dwStdHandle, HANDLE hNewHandle, HANDLE* phOldHandle); +WINPR_API BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters); + +WINPR_API BOOL GetDiskFreeSpaceW(LPCWSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters); + +WINPR_API BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags); + +WINPR_API BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags); + +WINPR_API BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName); + +WINPR_API BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName); + #ifdef __cplusplus } #endif @@ -326,6 +376,12 @@ WINPR_API BOOL SetStdHandleEx(DWORD dwStdHandle, HANDLE hNewHandle, HANDLE* phOl #define FindNextFile FindNextFileW #define CreateDirectory CreateDirectoryW #define RemoveDirectory RemoveDirectoryW +#define GetFileAttributesEx GetFileAttributesExW +#define GetFileAttributes GetFileAttributesW +#define SetFileAttributes SetFileAttributesW +#define GetDiskFreeSpace GetDiskFreeSpaceW +#define MoveFileEx MoveFileExW +#define MoveFile MoveFileW #else #define CreateFile CreateFileA #define DeleteFile DeleteFileA @@ -334,6 +390,12 @@ WINPR_API BOOL SetStdHandleEx(DWORD dwStdHandle, HANDLE hNewHandle, HANDLE* phOl #define FindNextFile FindNextFileA #define CreateDirectory CreateDirectoryA #define RemoveDirectory RemoveDirectoryA +#define GetFileAttributesEx GetFileAttributesExA +#define GetFileAttributes GetFileAttributesA +#define SetFileAttributes SetFileAttributesA +#define GetDiskFreeSpace GetDiskFreeSpaceA +#define MoveFileEx MoveFileExA +#define MoveFile MoveFileA #endif /* Extra Functions */ diff --git a/winpr/include/winpr/path.h b/winpr/include/winpr/path.h index 4575063ae..2e1902ebf 100644 --- a/winpr/include/winpr/path.h +++ b/winpr/include/winpr/path.h @@ -115,7 +115,6 @@ WINPR_API HRESULT PathCchStripPrefixW(PWSTR pszPath, size_t cchPath); WINPR_API HRESULT PathCchRemoveFileSpecA(PSTR pszPath, size_t cchPath); WINPR_API HRESULT PathCchRemoveFileSpecW(PWSTR pszPath, size_t cchPath); - #ifdef UNICODE #define PathCchAddBackslash PathCchAddBackslashW #define PathCchRemoveBackslash PathCchRemoveBackslashW @@ -292,10 +291,15 @@ WINPR_API BOOL PathMakePathA(LPCSTR path, LPSECURITY_ATTRIBUTES lpAttributes); WINPR_API BOOL PathFileExistsA(LPCSTR pszPath); WINPR_API BOOL PathFileExistsW(LPCWSTR pszPath); +WINPR_API BOOL PathIsDirectoryEmptyA(LPCSTR pszPath); +WINPR_API BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath); + #ifdef UNICODE #define PathFileExists PathFileExistsW +#define PathIsDirectoryEmpty PathIsDirectoryEmptyW #else #define PathFileExists PathFileExistsA +#define PathIsDirectoryEmpty PathIsDirectoryEmptyA #endif #endif diff --git a/winpr/include/winpr/shell.h b/winpr/include/winpr/shell.h index 5093925f8..ff6f5c2ef 100644 --- a/winpr/include/winpr/shell.h +++ b/winpr/include/winpr/shell.h @@ -3,6 +3,7 @@ * Shell Functions * * Copyright 2015 Dell Software + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -49,4 +50,3 @@ WINPR_API BOOL GetUserProfileDirectoryW(HANDLE hToken, LPWSTR lpProfileDir, LPDW #endif #endif /* WINPR_SHELL_H */ - diff --git a/winpr/include/winpr/string.h b/winpr/include/winpr/string.h index 5f4c26650..7ce889a51 100644 --- a/winpr/include/winpr/string.h +++ b/winpr/include/winpr/string.h @@ -3,6 +3,7 @@ * String Manipulation (CRT) * * Copyright 2012 Marc-Andre Moreau + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -58,6 +59,7 @@ WINPR_API int _wcscmp(const WCHAR* string1, const WCHAR* string2); WINPR_API size_t _wcslen(const WCHAR* str); WINPR_API WCHAR* _wcschr(const WCHAR* str, WCHAR c); +WINPR_API WCHAR* _wcsrchr(const WCHAR* str, WCHAR c); WINPR_API char* strtok_s(char* strToken, const char* strDelimit, char** context); @@ -69,6 +71,7 @@ WINPR_API WCHAR* wcstok_s(WCHAR* strToken, const WCHAR* strDelimit, #define _wcscmp wcscmp #define _wcslen wcslen #define _wcschr wcschr +#define _wcsrchr wcsrchr #endif diff --git a/winpr/libwinpr/crt/string.c b/winpr/libwinpr/crt/string.c index b7d36d90c..aa70212b7 100644 --- a/winpr/libwinpr/crt/string.c +++ b/winpr/libwinpr/crt/string.c @@ -129,6 +129,23 @@ WCHAR* _wcschr(const WCHAR* str, WCHAR c) return ((*p == value) ? p : NULL); } +/* _wcsrchr -> wcsrchr */ + +WCHAR* _wcsrchr(const WCHAR* str, WCHAR c) +{ + WCHAR *p; + WCHAR ch; + + if (!str) + return NULL; + + for (p = (WCHAR *) 0; (ch = *str); str++) + if (ch == c) + p = (WCHAR *) str; + + return p; +} + char* strtok_s(char* strToken, const char* strDelimit, char** context) { return strtok_r(strToken, strDelimit, context); diff --git a/winpr/libwinpr/file/file.c b/winpr/libwinpr/file/file.c index d42e3e658..24679c542 100644 --- a/winpr/libwinpr/file/file.c +++ b/winpr/libwinpr/file/file.c @@ -4,6 +4,7 @@ * * Copyright 2015 Thincast Technologies GmbH * Copyright 2015 Bernhard Miklautz + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +45,13 @@ #include #include +#ifdef ANDROID +#include +#else +#include +#endif + + static BOOL FileIsHandled(HANDLE handle) { WINPR_FILE* pFile = (WINPR_FILE*) handle; @@ -97,10 +105,12 @@ static BOOL FileSetEndOfFile(HANDLE hFile) return FALSE; size = ftell(pFile->fp); + if (ftruncate(fileno(pFile->fp), size) < 0) { WLog_ERR(TAG, "ftruncate %s failed with %s [0x%08X]", pFile->lpFileName, strerror(errno), errno); + SetLastError(map_posix_err(errno)); return FALSE; } @@ -161,9 +171,10 @@ static BOOL FileRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, return FALSE; file = (WINPR_FILE *)Object; - io_status = fread(lpBuffer, nNumberOfBytesToRead, 1, file->fp); + clearerr(file->fp); + io_status = fread(lpBuffer, 1, nNumberOfBytesToRead, file->fp); - if (io_status != 1) + if (io_status == 0 && ferror(file->fp)) { status = FALSE; @@ -172,11 +183,13 @@ static BOOL FileRead(PVOID Object, LPVOID lpBuffer, DWORD nNumberOfBytesToRead, case EWOULDBLOCK: SetLastError(ERROR_NO_DATA); break; + default: + SetLastError(map_posix_err(errno)); } } if (lpNumberOfBytesRead) - *lpNumberOfBytesRead = nNumberOfBytesToRead; + *lpNumberOfBytesRead = io_status; return status; } @@ -199,11 +212,15 @@ static BOOL FileWrite(PVOID Object, LPCVOID lpBuffer, DWORD nNumberOfBytesToWrit file = (WINPR_FILE *)Object; - io_status = fwrite(lpBuffer, nNumberOfBytesToWrite, 1, file->fp); - if (io_status != 1) + clearerr(file->fp); + io_status = fwrite(lpBuffer, 1, nNumberOfBytesToWrite, file->fp); + if (io_status == 0 && ferror(file->fp)) + { + SetLastError(map_posix_err(errno)); return FALSE; + } - *lpNumberOfBytesWritten = nNumberOfBytesToWrite; + *lpNumberOfBytesWritten = io_status; return TRUE; } @@ -507,7 +524,7 @@ static HANDLE_OPS shmOps = { static const char* FileGetMode(DWORD dwDesiredAccess, DWORD dwCreationDisposition, BOOL* create) { - BOOL writeable = dwDesiredAccess & GENERIC_WRITE; + BOOL writeable = (dwDesiredAccess & (GENERIC_WRITE | STANDARD_RIGHTS_WRITE | FILE_WRITE_DATA | FILE_APPEND_DATA)) != 0; switch(dwCreationDisposition) { @@ -522,7 +539,7 @@ static const char* FileGetMode(DWORD dwDesiredAccess, DWORD dwCreationDispositio return "rb+"; case OPEN_EXISTING: *create = FALSE; - return "rb+"; + return (writeable) ? "rb+" : "rb"; case TRUNCATE_EXISTING: *create = FALSE; return "wb+"; @@ -532,6 +549,51 @@ static const char* FileGetMode(DWORD dwDesiredAccess, DWORD dwCreationDispositio } } +UINT32 map_posix_err(int fs_errno) +{ + UINT32 rc; + + /* try to return NTSTATUS version of error code */ + + switch (fs_errno) + { + case 0: + rc = STATUS_SUCCESS; + break; + + case EPERM: + case EACCES: + rc = ERROR_ACCESS_DENIED; + break; + + case ENOENT: + rc = ERROR_FILE_NOT_FOUND; + break; + + case EBUSY: + rc = ERROR_BUSY_DRIVE; + break; + + case EEXIST: + rc = ERROR_FILE_EXISTS; + break; + + case EISDIR: + rc = STATUS_FILE_IS_A_DIRECTORY; + break; + + case ENOTEMPTY: + rc = STATUS_DIRECTORY_NOT_EMPTY; + break; + + default: + rc = STATUS_UNSUCCESSFUL; + break; + } + + return rc; +} + static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile) { @@ -540,6 +602,7 @@ static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dw const char* mode = FileGetMode(dwDesiredAccess, dwCreationDisposition, &create); int lock = 0; FILE* fp = NULL; + struct stat st; if (dwFlagsAndAttributes & FILE_FLAG_OVERLAPPED) { @@ -575,9 +638,21 @@ static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dw if (create) { + if (dwCreationDisposition == CREATE_NEW) + { + if (stat(pFile->lpFileName, &st) == 0) + { + SetLastError(ERROR_FILE_EXISTS); + free(pFile->lpFileName); + free(pFile); + return INVALID_HANDLE_VALUE; + } + } + fp = fopen(pFile->lpFileName, "ab"); if (!fp) { + SetLastError(map_posix_err(errno)); free(pFile->lpFileName); free(pFile); return INVALID_HANDLE_VALUE; @@ -594,6 +669,7 @@ static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dw { /* This case can occur when trying to open a * not existing file without create flag. */ + SetLastError(map_posix_err(errno)); free(pFile->lpFileName); free(pFile); return INVALID_HANDLE_VALUE; @@ -612,6 +688,7 @@ static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dw { WLog_ERR(TAG, "flock failed with %s [0x%08X]", strerror(errno), errno); + SetLastError(map_posix_err(errno)); FileCloseHandle(pFile); return INVALID_HANDLE_VALUE; } @@ -619,6 +696,13 @@ static HANDLE FileCreateFileA(LPCSTR lpFileName, DWORD dwDesiredAccess, DWORD dw pFile->bLocked = TRUE; } + if (fstat(fileno(pFile->fp), &st)==0 && dwFlagsAndAttributes & FILE_ATTRIBUTE_READONLY) + { + st.st_mode &= ~(S_IWUSR|S_IWGRP|S_IWOTH); + fchmod(fileno(pFile->fp), st.st_mode); + } + + SetLastError(STATUS_SUCCESS); return pFile; } @@ -695,6 +779,41 @@ BOOL SetStdHandleEx(DWORD dwStdHandle, HANDLE hNewHandle, HANDLE* phOldHandle) return FALSE; } +BOOL GetDiskFreeSpaceA(LPCSTR lpRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) +{ +#if defined(ANDROID) +#define STATVFS statfs +#else +#define STATVFS statvfs +#endif + + struct STATVFS svfst; + STATVFS(lpRootPathName, &svfst); + *lpSectorsPerCluster = svfst.f_frsize; + *lpBytesPerSector = 1; + *lpNumberOfFreeClusters = svfst.f_bavail; + *lpTotalNumberOfClusters = svfst.f_blocks; + return TRUE; +} + +BOOL GetDiskFreeSpaceW(LPCWSTR lpwRootPathName, LPDWORD lpSectorsPerCluster, + LPDWORD lpBytesPerSector, LPDWORD lpNumberOfFreeClusters, LPDWORD lpTotalNumberOfClusters) +{ + LPSTR lpRootPathName; + BOOL ret; + + if (ConvertFromUnicode(CP_UTF8, 0, lpwRootPathName, -1, &lpRootPathName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + ret = GetDiskFreeSpaceA(lpRootPathName, lpSectorsPerCluster, lpBytesPerSector, + lpNumberOfFreeClusters, lpTotalNumberOfClusters); + free(lpRootPathName); + return ret; +} + #endif /* _WIN32 */ #ifdef _UWP diff --git a/winpr/libwinpr/file/file.h b/winpr/libwinpr/file/file.h index af0916753..d7dfe1dc8 100644 --- a/winpr/libwinpr/file/file.h +++ b/winpr/libwinpr/file/file.h @@ -4,6 +4,7 @@ * * Copyright 2015 Armin Novak * Copyright 2015 Thincast Technologies GmbH + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -55,6 +56,8 @@ typedef struct winpr_file WINPR_FILE; HANDLE_CREATOR *GetFileHandleCreator(void); +UINT32 map_posix_err(int fs_errno); + #endif /* _WIN32 */ #endif /* WINPR_FILE_PRIV_H */ diff --git a/winpr/libwinpr/file/generic.c b/winpr/libwinpr/file/generic.c index 1f2dc0508..07902d7ae 100644 --- a/winpr/libwinpr/file/generic.c +++ b/winpr/libwinpr/file/generic.c @@ -4,6 +4,7 @@ * * Copyright 2012 Marc-Andre Moreau * Copyright 2014 Hewlett-Packard Development Company, L.P. + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -44,6 +45,8 @@ #include #include #include +#include +#include #include #include @@ -173,6 +176,10 @@ * http://code.google.com/p/kernel/wiki/AIOUserGuide */ +#define EPOCH_DIFF 11644473600LL +#define STAT_TIME_TO_FILETIME(_t) (((UINT64)(_t) + EPOCH_DIFF) * 10000000LL) + + static wArrayList *_HandleCreators; static pthread_once_t _HandleCreatorsInitialized = PTHREAD_ONCE_INIT; @@ -461,6 +468,112 @@ BOOL FlushFileBuffers(HANDLE hFile) return FALSE; } +BOOL WINAPI GetFileAttributesExA(LPCSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFileInformation) +{ + LPWIN32_FILE_ATTRIBUTE_DATA fd = lpFileInformation; + WIN32_FIND_DATAA findFileData; + HANDLE hFind; + + if ((hFind = FindFirstFileA(lpFileName, &findFileData) != INVALID_HANDLE_VALUE)) + FindClose(hFind); + + fd->dwFileAttributes = findFileData.dwFileAttributes; + fd->ftCreationTime = findFileData.ftCreationTime; + fd->ftLastAccessTime = findFileData.ftLastAccessTime; + fd->ftLastWriteTime = findFileData.ftLastWriteTime; + fd->nFileSizeHigh = findFileData.nFileSizeHigh; + fd->nFileSizeLow = findFileData.nFileSizeLow; + return TRUE; +} + +BOOL WINAPI GetFileAttributesExW(LPCWSTR lpFileName, GET_FILEEX_INFO_LEVELS fInfoLevelId, + LPVOID lpFileInformation) +{ + BOOL ret; + LPSTR lpCFileName; + + if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = GetFileAttributesExA(lpCFileName, fInfoLevelId, lpFileInformation); + free(lpCFileName); + return ret; +} + +DWORD WINAPI GetFileAttributesA(LPCSTR lpFileName) +{ + WIN32_FIND_DATAA findFileData; + HANDLE hFind; + + if ((hFind = FindFirstFileA(lpFileName, &findFileData)) == INVALID_HANDLE_VALUE) + return INVALID_FILE_ATTRIBUTES; + + FindClose(hFind); + return findFileData.dwFileAttributes; +} + +DWORD WINAPI GetFileAttributesW(LPCWSTR lpFileName) +{ + DWORD ret; + LPSTR lpCFileName; + + if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = GetFileAttributesA(lpCFileName); + free(lpCFileName); + return ret; +} + +BOOL SetFileAttributesA(LPCSTR lpFileName, DWORD dwFileAttributes) +{ + struct stat st; + + if (stat(lpFileName, &st) != 0) + { + return FALSE; + } + + if (dwFileAttributes & FILE_ATTRIBUTE_READONLY) + { + st.st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH); + } + else + { + st.st_mode |= S_IWUSR; + } + + if (chmod(lpFileName, st.st_mode) != 0) + { + return FALSE; + } + + return TRUE; +} + +BOOL SetFileAttributesW(LPCWSTR lpFileName, DWORD dwFileAttributes) +{ + BOOL ret; + LPSTR lpCFileName; + + if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &lpCFileName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = SetFileAttributesA(lpCFileName,dwFileAttributes); + free(lpCFileName); + return ret; +} + BOOL SetEndOfFile(HANDLE hFile) { ULONG Type; @@ -721,20 +834,8 @@ HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData) return INVALID_HANDLE_VALUE; /* failed to open directory */ } - while ((pFileSearch->pDirent = readdir(pFileSearch->pDir)) != NULL) - { - if ((strcmp(pFileSearch->pDirent->d_name, ".") == 0) || (strcmp(pFileSearch->pDirent->d_name, "..") == 0)) - { - /* skip "." and ".." */ - continue; - } - - if (FilePatternMatchA(pFileSearch->pDirent->d_name, pFileSearch->lpPattern)) - { - strcpy(lpFindFileData->cFileName, pFileSearch->pDirent->d_name); - return (HANDLE) pFileSearch; - } - } + if (FindNextFileA((HANDLE) pFileSearch, lpFindFileData)) + return (HANDLE) pFileSearch; FindClose(pFileSearch); return INVALID_HANDLE_VALUE; @@ -742,24 +843,68 @@ HANDLE FindFirstFileA(LPCSTR lpFileName, LPWIN32_FIND_DATAA lpFindFileData) HANDLE FindFirstFileW(LPCWSTR lpFileName, LPWIN32_FIND_DATAW lpFindFileData) { - return NULL; + char* utfFileName = NULL; + HANDLE h; + WCHAR* unicodeFileName; + int length; + + LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)malloc(sizeof(WIN32_FIND_DATAA)); + if (!fd) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return INVALID_HANDLE_VALUE; + } + + if (ConvertFromUnicode(CP_UTF8, 0, lpFileName, -1, &utfFileName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return INVALID_HANDLE_VALUE; + } + + h = FindFirstFileA(utfFileName, fd); + free(utfFileName); + + if (h != INVALID_HANDLE_VALUE) + { + CopyMemory(lpFindFileData, fd, 352); + + unicodeFileName = NULL; + length = ConvertToUnicode(CP_UTF8, 0, fd->cFileName, -1, &unicodeFileName, 0) * 2; + if (length == 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + free(fd); + return INVALID_HANDLE_VALUE; + } + CopyMemory(&lpFindFileData->cFileName, unicodeFileName, length); + free(unicodeFileName); + } + + return h; } HANDLE FindFirstFileExA(LPCSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) { - return NULL; + return INVALID_HANDLE_VALUE; } HANDLE FindFirstFileExW(LPCWSTR lpFileName, FINDEX_INFO_LEVELS fInfoLevelId, LPVOID lpFindFileData, FINDEX_SEARCH_OPS fSearchOp, LPVOID lpSearchFilter, DWORD dwAdditionalFlags) { - return NULL; + return INVALID_HANDLE_VALUE; } BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) { WIN32_FILE_SEARCH* pFileSearch; + struct stat fileStat; + char* fullpath; + int pathlen; + int namelen; + UINT64 ft; + + ZeroMemory(lpFindFileData, sizeof(WIN32_FIND_DATAA)); if (!hFindFile) return FALSE; @@ -774,15 +919,103 @@ BOOL FindNextFileA(HANDLE hFindFile, LPWIN32_FIND_DATAA lpFindFileData) if (FilePatternMatchA(pFileSearch->pDirent->d_name, pFileSearch->lpPattern)) { strcpy(lpFindFileData->cFileName, pFileSearch->pDirent->d_name); + namelen = strlen(lpFindFileData->cFileName); + + pathlen = strlen(pFileSearch->lpPath); + fullpath = (char*)malloc(pathlen + namelen + 2); + if (fullpath == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + memcpy(fullpath, pFileSearch->lpPath, pathlen); + fullpath[pathlen] = '/'; + memcpy(fullpath + pathlen + 1, pFileSearch->pDirent->d_name, namelen); + fullpath[pathlen+namelen+1] = 0; + + if (lstat(fullpath, &fileStat) != 0) + { + free(fullpath); + SetLastError(map_posix_err(errno)); + return FALSE; + } + + free(fullpath); + + lpFindFileData->dwFileAttributes = 0; + + if (S_ISDIR(fileStat.st_mode)) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_DIRECTORY; + + if (lpFindFileData->dwFileAttributes == 0) + lpFindFileData->dwFileAttributes = FILE_ATTRIBUTE_ARCHIVE; + + if (pFileSearch->pDirent->d_name[0] == '.' && namelen != 1 && + (pFileSearch->pDirent->d_name[1] != '.' && namelen != 2)) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_HIDDEN; + + if (! (fileStat.st_mode & S_IWUSR)) + lpFindFileData->dwFileAttributes |= FILE_ATTRIBUTE_READONLY; + +#ifdef _DARWIN_FEATURE_64_BIT_INODE + ft = STAT_TIME_TO_FILETIME(fileStat.st_birthtime); +#else + ft = STAT_TIME_TO_FILETIME(fileStat.st_ctime); +#endif + lpFindFileData->ftCreationTime.dwHighDateTime = ft >> 32; + lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF; + + ft = STAT_TIME_TO_FILETIME(fileStat.st_mtime); + lpFindFileData->ftLastWriteTime.dwHighDateTime = ft >> 32; + lpFindFileData->ftCreationTime.dwLowDateTime = ft & 0xFFFFFFFF; + + ft = STAT_TIME_TO_FILETIME(fileStat.st_atime); + lpFindFileData->ftLastAccessTime.dwHighDateTime = ft >> 32; + lpFindFileData->ftLastAccessTime.dwLowDateTime = ft & 0xFFFFFFFF; + + lpFindFileData->nFileSizeHigh = fileStat.st_size >> 32; + lpFindFileData->nFileSizeLow = fileStat.st_size & 0xFFFFFFFF; + return TRUE; } } + SetLastError(ERROR_NO_MORE_FILES); return FALSE; } BOOL FindNextFileW(HANDLE hFindFile, LPWIN32_FIND_DATAW lpFindFileData) { + WCHAR* unicodeFileName; + int length; + + LPWIN32_FIND_DATAA fd = (LPWIN32_FIND_DATAA)malloc(sizeof(WIN32_FIND_DATAA)); + if (!fd) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + if (FindNextFileA(hFindFile, fd)) + { + CopyMemory(lpFindFileData, fd, 352); + + unicodeFileName = NULL; + length = ConvertToUnicode(CP_UTF8, 0, fd->cFileName, -1, &unicodeFileName, 0) * 2; + if (length == 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + free(fd); + return FALSE; + } + CopyMemory(&lpFindFileData->cFileName, unicodeFileName, length); + free(unicodeFileName); + free(fd); + + return TRUE; + } + + free(fd); return FALSE; } @@ -816,17 +1049,109 @@ BOOL CreateDirectoryA(LPCSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttribu BOOL CreateDirectoryW(LPCWSTR lpPathName, LPSECURITY_ATTRIBUTES lpSecurityAttributes) { - return FALSE; + char* utfPathName = NULL; + BOOL ret; + + if (ConvertFromUnicode(CP_UTF8, 0, lpPathName, -1, &utfPathName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = CreateDirectoryA(utfPathName, lpSecurityAttributes); + free(utfPathName); + return ret; } BOOL RemoveDirectoryA(LPCSTR lpPathName) { - return (rmdir(lpPathName) == 0); + int ret = rmdir(lpPathName); + if (ret != 0) + SetLastError(map_posix_err(errno)); + else + SetLastError(STATUS_SUCCESS); + return ret == 0; } BOOL RemoveDirectoryW(LPCWSTR lpPathName) { - return FALSE; + char* utfPathName = NULL; + BOOL ret; + + if (ConvertFromUnicode(CP_UTF8, 0, lpPathName, -1, &utfPathName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = RemoveDirectoryA(utfPathName); + free(utfPathName); + return ret; +} + +BOOL MoveFileExA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName, DWORD dwFlags) +{ + struct stat st; + int ret; + ret = stat(lpNewFileName, &st); + + if ((dwFlags & MOVEFILE_REPLACE_EXISTING) == 0) + { + if (ret == 0) + { + SetLastError(ERROR_ALREADY_EXISTS); + return FALSE; + } + } + else + { + if (ret == 0 && (st.st_mode & S_IWUSR) == 0) + { + SetLastError(ERROR_ACCESS_DENIED); + return FALSE; + } + } + + ret = rename(lpExistingFileName, lpNewFileName); + + if (ret != 0) + SetLastError(map_posix_err(errno)); + + return ret == 0; +} + +BOOL MoveFileExW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName, DWORD dwFlags) +{ + LPSTR lpCExistingFileName; + LPSTR lpCNewFileName; + BOOL ret; + if (ConvertFromUnicode(CP_UTF8, 0, lpExistingFileName, -1, &lpCExistingFileName, 0, NULL, NULL) <= 0) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + if (ConvertFromUnicode(CP_UTF8, 0, lpNewFileName, -1, &lpCNewFileName, 0, NULL, NULL) <= 0) + { + free(lpCExistingFileName); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + ret = MoveFileExA(lpCExistingFileName, lpCNewFileName, dwFlags); + free(lpCNewFileName); + free(lpCExistingFileName); + return ret; +} + +BOOL MoveFileA(LPCSTR lpExistingFileName, LPCSTR lpNewFileName) +{ + return MoveFileExA(lpExistingFileName, lpNewFileName, 0); +} + +BOOL MoveFileW(LPCWSTR lpExistingFileName, LPCWSTR lpNewFileName) +{ + return MoveFileExW(lpExistingFileName, lpNewFileName, 0); } #endif diff --git a/winpr/libwinpr/path/shell.c b/winpr/libwinpr/path/shell.c index 386cac37d..39a5e5642 100644 --- a/winpr/libwinpr/path/shell.c +++ b/winpr/libwinpr/path/shell.c @@ -3,6 +3,7 @@ * Path Functions * * Copyright 2012 Marc-Andre Moreau + * Copyright 2016 David PHAM-VAN * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,6 +44,7 @@ #include #else #include +#include #endif static char* GetPath_XDG_CONFIG_HOME(void); @@ -505,9 +507,54 @@ BOOL PathFileExistsA(LPCSTR pszPath) BOOL PathFileExistsW(LPCWSTR pszPath) { - return FALSE; + LPSTR lpFileNameA = NULL; + BOOL ret; + + if (ConvertFromUnicode(CP_UTF8, 0, pszPath, -1, &lpFileNameA, 0, NULL, NULL) < 1) + return FALSE; + + ret = PathFileExistsA(lpFileNameA); + free (lpFileNameA); + + return ret; } +BOOL PathIsDirectoryEmptyA(LPCSTR pszPath) +{ + struct dirent *dp; + int empty = 1; + + DIR *dir = opendir(pszPath); + if (dir == NULL) /* Not a directory or doesn't exist */ + return 1; + + while ((dp = readdir(dir)) != NULL) { + if (strcmp(dp->d_name, ".") == 0 || strcmp(dp->d_name, "..") == 0) + continue; /* Skip . and .. */ + + empty = 0; + break; + } + closedir(dir); + return empty; +} + + +BOOL PathIsDirectoryEmptyW(LPCWSTR pszPath) +{ + LPSTR lpFileNameA = NULL; + BOOL ret; + + if (ConvertFromUnicode(CP_UTF8, 0, pszPath, -1, &lpFileNameA, 0, NULL, NULL) < 1) + return FALSE; + + ret = PathIsDirectoryEmptyA(lpFileNameA); + free (lpFileNameA); + + return ret; +} + + #else #ifdef _WIN32