diff --git a/aegisub/libaegisub/windows/access.cpp b/aegisub/libaegisub/windows/access.cpp index 4264aefd6..0c9efec32 100644 --- a/aegisub/libaegisub/windows/access.cpp +++ b/aegisub/libaegisub/windows/access.cpp @@ -19,25 +19,14 @@ /// @ingroup libaegisub windows #ifndef LAGI_PRE -#include -#include -#include +#include #include #include #endif -#ifndef R_OK -#define R_OK 04 -#endif -#ifndef W_OK -#define W_OK 02 -#endif - -#pragma warning(disable: 4996) - - #include "libaegisub/util.h" +#include "libaegisub/util_win.h" namespace agi { namespace acs { @@ -61,24 +50,43 @@ void CheckDirWrite(const std::string &dir) { Check(dir, acs::DirWrite); } - +/* +This function is still a proof of concept, it's probably rife with bugs, below +is a short (and incomplete) todo + * "Basic" checks (Read/Write/File/Dir) checks for FAT32 filesystems which + requires detecting the filesystem being used. +*/ void Check(const std::string &file, acs::Type type) { - struct stat file_stat; - int file_status; + std::wstring wfile; + wfile.assign(file.begin(), file.end()); - file_status = stat(file.c_str(), &file_stat); + SECURITY_DESCRIPTOR* sd; + DWORD len = 0; + SECURITY_INFORMATION info = OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION; + HANDLE client_token; + PRIVILEGE_SET priv_set; + DWORD priv_set_size = sizeof(PRIVILEGE_SET); + BOOL access_ok; + GENERIC_MAPPING generic_mapping; + DWORD access_check; + DWORD access; + DWORD file_attr; - if (file_status != 0) { - switch (errno) { - case ENOENT: + file_attr = GetFileAttributes(wfile.c_str()); + + if ((file_attr & INVALID_FILE_ATTRIBUTES) == INVALID_FILE_ATTRIBUTES) { + + switch (GetLastError()) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: throw AcsNotFound("File or path not found."); break; - case EACCES: - throw AcsAccess("Access Denied to file, path or path component."); + case ERROR_ACCESS_DENIED: + throw AcsAccess("Access denied to file or path component"); break; - case EIO: + default: throw AcsFatal("Fatal I/O error occurred."); break; } @@ -86,33 +94,71 @@ void Check(const std::string &file, acs::Type type) { switch (type) { case FileRead: - case FileWrite: - if ((file_stat.st_mode & S_IFREG) == 0) + case FileWrite: { + if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) throw AcsNotAFile("Not a file."); - break; - + break; + } case DirRead: - case DirWrite: - if ((file_stat.st_mode & S_IFDIR) == 0) + case DirWrite: { + if ((file_attr & FILE_ATTRIBUTE_DIRECTORY) != FILE_ATTRIBUTE_DIRECTORY) throw AcsNotADirectory("Not a directory."); - break; + break; + } + } + + GetFileSecurity(wfile.c_str(), info, NULL, 0, &len); + if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { + std::cout << "GetFileSecurity: fatal: " << util::ErrorString(GetLastError()) << std::endl; + } + + sd = (SECURITY_DESCRIPTOR *)malloc(len); + if (sd == NULL) { + std::cout << "GetFileSecurity: insufficient memory" << std::endl; + } else { + if (!GetFileSecurity(wfile.c_str(), info, sd, len, &len)) { + std::cout << "GetFileSecurity failed: " << util::ErrorString(GetLastError()) << std::endl; + free(sd); + } + } + + ImpersonateSelf(SecurityImpersonation); + if (!OpenThreadToken(GetCurrentThread(), TOKEN_ALL_ACCESS, TRUE, &client_token)) { + std::cout << "OpenThreadToken failed: " << util::ErrorString(GetLastError()) << std::endl; } switch (type) { case DirRead: - case FileRead: - file_status = access(file.c_str(), R_OK); - if (file_status != 0) + case FileRead: { + access_check = FILE_READ_DATA; + MapGenericMask(&access_check, &generic_mapping); + if(!AccessCheck(sd, client_token, access_check, &generic_mapping, &priv_set, &priv_set_size, &access, &access_ok)) { + std::cout << "AccessCheck failed: " << util::ErrorString(GetLastError()) << std::endl; + } + free(sd); + if (!access) throw AcsRead("File or directory is not readable."); - break; - + break; + } case DirWrite: - case FileWrite: - file_status = access(file.c_str(), W_OK); - if (file_status != 0) + case FileWrite: { + access_check = FILE_APPEND_DATA | FILE_WRITE_DATA; + MapGenericMask(&access_check, &generic_mapping); + if(!AccessCheck(sd, client_token, access_check, &generic_mapping, &priv_set, &priv_set_size, &access, &access_ok)) { + std::cout << "AccessCheck failed: " << util::ErrorString(GetLastError()) << std::endl; + } + free(sd); + if (!access) throw AcsWrite("File or directory is not writable."); - break; - } -} + break; + } + default: { + std::cout << "Warning: type not handled" << std::endl; + free(sd); + } } + } + + } // namespace Access +} // namespace agi