mirror of
https://github.com/LIV2/WinUAE.git
synced 2025-12-06 00:12:52 +00:00
1057 lines
27 KiB
C++
1057 lines
27 KiB
C++
#include "sysconfig.h"
|
|
#include "sysdeps.h"
|
|
#include "options.h"
|
|
#include "memory.h"
|
|
|
|
#include "fsdb.h"
|
|
#include "zfile.h"
|
|
#include "win32.h"
|
|
|
|
#include <windows.h>
|
|
#include <sys/timeb.h>
|
|
#include <Shobjidl.h>
|
|
#include <ShlGuid.h>
|
|
#include <Shlwapi.h>
|
|
|
|
bool my_isfilehidden (const TCHAR *path)
|
|
{
|
|
DWORD attr = GetFileAttributes (path);
|
|
if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_HIDDEN))
|
|
return true;
|
|
return false;
|
|
}
|
|
void my_setfilehidden (const TCHAR *path, bool hidden)
|
|
{
|
|
DWORD attr = GetFileAttributes (path);
|
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
|
return;
|
|
DWORD attro = attr;
|
|
attr &= ~FILE_ATTRIBUTE_HIDDEN;
|
|
if (hidden)
|
|
attr |= FILE_ATTRIBUTE_HIDDEN;
|
|
if (attro == attr)
|
|
return;
|
|
SetFileAttributes (path, attr);
|
|
}
|
|
|
|
int my_setcurrentdir (const TCHAR *curdir, TCHAR *oldcur)
|
|
{
|
|
int ret = 0;
|
|
if (oldcur)
|
|
ret = GetCurrentDirectory (MAX_DPATH, oldcur);
|
|
if (curdir) {
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, curdir);
|
|
namep = path;
|
|
} else {
|
|
namep = curdir;
|
|
}
|
|
ret = SetCurrentDirectory (namep);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int my_mkdir (const TCHAR *name)
|
|
{
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
return CreateDirectory (namep, NULL) == 0 ? -1 : 0;
|
|
}
|
|
|
|
static int recycle (const TCHAR *name, bool dontrecycle)
|
|
{
|
|
DWORD dirattr = GetFileAttributesSafe (name);
|
|
bool isdir = dirattr != INVALID_FILE_ATTRIBUTES && (dirattr & FILE_ATTRIBUTE_DIRECTORY);
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
|
|
if (dontrecycle || currprefs.win32_norecyclebin || isdir || currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
if (isdir)
|
|
return RemoveDirectory (namep) ? 0 : -1;
|
|
else
|
|
return DeleteFile (namep) ? 0 : -1;
|
|
} else {
|
|
SHFILEOPSTRUCT fos;
|
|
HANDLE h;
|
|
|
|
h = CreateFile (namep, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (h != INVALID_HANDLE_VALUE) {
|
|
LARGE_INTEGER size;
|
|
if (GetFileSizeEx (h, &size)) {
|
|
if (size.QuadPart == 0) {
|
|
CloseHandle (h);
|
|
return DeleteFile (namep) ? 0 : -1;
|
|
}
|
|
}
|
|
CloseHandle (h);
|
|
}
|
|
|
|
/* name must be terminated by \0\0 */
|
|
TCHAR *p = xcalloc (TCHAR, _tcslen (namep) + 2);
|
|
int v;
|
|
|
|
_tcscpy (p, namep);
|
|
memset (&fos, 0, sizeof (fos));
|
|
fos.wFunc = FO_DELETE;
|
|
fos.pFrom = p;
|
|
fos.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_NORECURSION | FOF_SILENT;
|
|
v = SHFileOperation (&fos);
|
|
xfree (p);
|
|
switch (v)
|
|
{
|
|
case 0xb7: //DE_ERROR_MAX
|
|
case 0x7c: //DE_INVALIDFILES
|
|
case 0x402: // "unknown error"
|
|
v = ERROR_FILE_NOT_FOUND;
|
|
break;
|
|
case 0x75: //DE_OPCANCELLED:
|
|
case 0x10000: //ERRORONDEST:
|
|
case 0x78: //DE_ACCESSDENIEDSRC:
|
|
case 0x74: //DE_ROOTDIR:
|
|
v = ERROR_ACCESS_DENIED;
|
|
break;
|
|
}
|
|
SetLastError (v);
|
|
return v ? -1 : 0;
|
|
}
|
|
}
|
|
|
|
int my_rmdir (const TCHAR *name)
|
|
{
|
|
struct my_opendir_s *od;
|
|
int cnt;
|
|
TCHAR tname[MAX_DPATH];
|
|
|
|
/* SHFileOperation() ignores FOF_NORECURSION when deleting directories.. */
|
|
od = my_opendir (name);
|
|
if (!od) {
|
|
SetLastError (ERROR_FILE_NOT_FOUND);
|
|
return -1;
|
|
}
|
|
cnt = 0;
|
|
while (my_readdir (od, tname)) {
|
|
if (!_tcscmp (tname, _T(".")) || !_tcscmp (tname, _T("..")))
|
|
continue;
|
|
cnt++;
|
|
break;
|
|
}
|
|
my_closedir (od);
|
|
if (cnt > 0) {
|
|
SetLastError (ERROR_CURRENT_DIRECTORY);
|
|
return -1;
|
|
}
|
|
|
|
return recycle (name, false);
|
|
}
|
|
|
|
/* "move to Recycle Bin" (if enabled) -version of DeleteFile() */
|
|
int my_unlink (const TCHAR *name, bool dontrecycle)
|
|
{
|
|
return recycle (name, dontrecycle);
|
|
}
|
|
|
|
int my_rename (const TCHAR *oldname, const TCHAR *newname)
|
|
{
|
|
const TCHAR *onamep, *nnamep;
|
|
TCHAR opath[MAX_DPATH], npath[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (opath, PATHPREFIX);
|
|
_tcscat (opath, oldname);
|
|
onamep = opath;
|
|
_tcscpy (npath, PATHPREFIX);
|
|
_tcscat (npath, newname);
|
|
nnamep = npath;
|
|
} else {
|
|
onamep = oldname;
|
|
nnamep = newname;
|
|
}
|
|
return MoveFile (onamep, nnamep) == 0 ? -1 : 0;
|
|
}
|
|
|
|
struct my_opendir_s {
|
|
HANDLE h;
|
|
WIN32_FIND_DATA fd;
|
|
int first;
|
|
};
|
|
|
|
struct my_opendir_s *my_opendir (const TCHAR *name)
|
|
{
|
|
return my_opendir (name, _T("*.*"));
|
|
}
|
|
struct my_opendir_s *my_opendir (const TCHAR *name, const TCHAR *mask)
|
|
{
|
|
struct my_opendir_s *mod;
|
|
TCHAR tmp[MAX_DPATH];
|
|
|
|
tmp[0] = 0;
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false)
|
|
_tcscpy (tmp, PATHPREFIX);
|
|
_tcscat (tmp, name);
|
|
_tcscat (tmp, _T("\\"));
|
|
_tcscat (tmp, mask);
|
|
mod = xmalloc (struct my_opendir_s, 1);
|
|
if (!mod)
|
|
return NULL;
|
|
mod->h = FindFirstFile(tmp, &mod->fd);
|
|
if (mod->h == INVALID_HANDLE_VALUE) {
|
|
xfree (mod);
|
|
return NULL;
|
|
}
|
|
mod->first = 1;
|
|
return mod;
|
|
}
|
|
|
|
void my_closedir (struct my_opendir_s *mod)
|
|
{
|
|
if (mod)
|
|
FindClose (mod->h);
|
|
xfree (mod);
|
|
}
|
|
|
|
int my_readdir (struct my_opendir_s *mod, TCHAR *name)
|
|
{
|
|
if (mod->first) {
|
|
_tcscpy (name, mod->fd.cFileName);
|
|
mod->first = 0;
|
|
return 1;
|
|
}
|
|
if (!FindNextFile (mod->h, &mod->fd))
|
|
return 0;
|
|
_tcscpy (name, mod->fd.cFileName);
|
|
return 1;
|
|
}
|
|
|
|
struct my_openfile_s {
|
|
HANDLE h;
|
|
};
|
|
|
|
void my_close (struct my_openfile_s *mos)
|
|
{
|
|
CloseHandle (mos->h);
|
|
xfree (mos);
|
|
}
|
|
|
|
uae_s64 int my_lseek (struct my_openfile_s *mos, uae_s64 int offset, int whence)
|
|
{
|
|
LARGE_INTEGER li, old;
|
|
|
|
old.QuadPart = 0;
|
|
old.LowPart = SetFilePointer (mos->h, 0, &old.HighPart, FILE_CURRENT);
|
|
if (old.LowPart == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)
|
|
return -1;
|
|
if (offset == 0 && whence == SEEK_CUR)
|
|
return old.QuadPart;
|
|
li.QuadPart = offset;
|
|
li.LowPart = SetFilePointer (mos->h, li.LowPart, &li.HighPart,
|
|
whence == SEEK_SET ? FILE_BEGIN : (whence == SEEK_END ? FILE_END : FILE_CURRENT));
|
|
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR)
|
|
return -1;
|
|
return old.QuadPart;
|
|
}
|
|
uae_s64 int my_fsize (struct my_openfile_s *mos)
|
|
{
|
|
LARGE_INTEGER li;
|
|
if (!GetFileSizeEx (mos->h, &li))
|
|
return -1;
|
|
return li.QuadPart;
|
|
}
|
|
|
|
|
|
unsigned int my_read (struct my_openfile_s *mos, void *b, unsigned int size)
|
|
{
|
|
DWORD read = 0;
|
|
ReadFile (mos->h, b, size, &read, NULL);
|
|
return read;
|
|
}
|
|
|
|
unsigned int my_write (struct my_openfile_s *mos, void *b, unsigned int size)
|
|
{
|
|
DWORD written = 0;
|
|
WriteFile (mos->h, b, size, &written, NULL);
|
|
return written;
|
|
}
|
|
|
|
BOOL SetFileAttributesSafe (const TCHAR *name, DWORD attr)
|
|
{
|
|
DWORD last;
|
|
BOOL ret;
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
last = SetErrorMode (SEM_FAILCRITICALERRORS);
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
ret = SetFileAttributes (namep, attr);
|
|
SetErrorMode (last);
|
|
return ret;
|
|
}
|
|
|
|
DWORD GetFileAttributesSafe (const TCHAR *name)
|
|
{
|
|
DWORD attr, last;
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
last = SetErrorMode (SEM_FAILCRITICALERRORS);
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
attr = GetFileAttributes (namep);
|
|
SetErrorMode (last);
|
|
return attr;
|
|
}
|
|
|
|
int my_existsfile (const TCHAR *name)
|
|
{
|
|
DWORD attr;
|
|
|
|
attr = GetFileAttributesSafe (name);
|
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
|
return 0;
|
|
if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int my_existsdir (const TCHAR *name)
|
|
{
|
|
DWORD attr;
|
|
|
|
attr = GetFileAttributesSafe (name);
|
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
|
return 0;
|
|
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
|
return 1;
|
|
return 0;
|
|
}
|
|
|
|
int my_readonlyfile(const TCHAR *name)
|
|
{
|
|
HANDLE h;
|
|
|
|
if (!my_existsfile(name))
|
|
return -1;
|
|
h = CreateFile(name, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
DWORD err = GetLastError();
|
|
if (err == ERROR_ACCESS_DENIED || err == ERROR_WRITE_PROTECT)
|
|
return true;
|
|
}
|
|
CloseHandle(h);
|
|
return false;
|
|
}
|
|
|
|
struct my_openfile_s *my_open (const TCHAR *name, int flags)
|
|
{
|
|
struct my_openfile_s *mos;
|
|
HANDLE h;
|
|
DWORD DesiredAccess = GENERIC_READ;
|
|
DWORD ShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
|
|
DWORD CreationDisposition = OPEN_EXISTING;
|
|
DWORD FlagsAndAttributes = FILE_ATTRIBUTE_NORMAL;
|
|
DWORD attr;
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
mos = xmalloc (struct my_openfile_s, 1);
|
|
if (!mos)
|
|
return NULL;
|
|
attr = GetFileAttributesSafe (name);
|
|
if (flags & O_TRUNC)
|
|
CreationDisposition = CREATE_ALWAYS;
|
|
else if (flags & O_CREAT)
|
|
CreationDisposition = OPEN_ALWAYS;
|
|
if (flags & O_WRONLY)
|
|
DesiredAccess = GENERIC_WRITE;
|
|
if (flags == O_RDONLY) {
|
|
DesiredAccess = GENERIC_READ;
|
|
CreationDisposition = OPEN_EXISTING;
|
|
}
|
|
if (flags & O_RDWR)
|
|
DesiredAccess = GENERIC_READ | GENERIC_WRITE;
|
|
if (CreationDisposition == CREATE_ALWAYS && attr != INVALID_FILE_ATTRIBUTES && (attr & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)))
|
|
SetFileAttributesSafe (name, FILE_ATTRIBUTE_NORMAL);
|
|
h = CreateFile (namep, DesiredAccess, ShareMode, NULL, CreationDisposition, FlagsAndAttributes, NULL);
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
DWORD err = GetLastError();
|
|
if (err == ERROR_ACCESS_DENIED && (DesiredAccess & GENERIC_WRITE)) {
|
|
DesiredAccess &= ~GENERIC_WRITE;
|
|
h = CreateFile (namep, DesiredAccess, ShareMode, NULL, CreationDisposition, FlagsAndAttributes, NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
err = GetLastError();
|
|
}
|
|
if (h == INVALID_HANDLE_VALUE) {
|
|
write_log (_T("failed to open '%s' %x %x err=%d\n"), namep, DesiredAccess, CreationDisposition, err);
|
|
xfree (mos);
|
|
mos = NULL;
|
|
goto err;
|
|
}
|
|
}
|
|
mos->h = h;
|
|
err:
|
|
//write_log (_T("open '%s' = %x\n"), namep, mos ? mos->h : 0);
|
|
return mos;
|
|
}
|
|
|
|
int my_truncate (const TCHAR *name, uae_u64 len)
|
|
{
|
|
HANDLE hFile;
|
|
BOOL bResult = FALSE;
|
|
int result = -1;
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
if ((hFile = CreateFile (namep, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
|
|
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ) ) != INVALID_HANDLE_VALUE )
|
|
{
|
|
LARGE_INTEGER li;
|
|
li.QuadPart = len;
|
|
li.LowPart = SetFilePointer (hFile, li.LowPart, &li.HighPart, FILE_BEGIN);
|
|
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError () != NO_ERROR) {
|
|
write_log (_T("truncate: SetFilePointer() failure for %s to posn %d\n"), namep, len);
|
|
} else {
|
|
if (SetEndOfFile (hFile) == TRUE)
|
|
result = 0;
|
|
}
|
|
CloseHandle (hFile);
|
|
} else {
|
|
write_log (_T("truncate: CreateFile() failed to open %s\n"), namep);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
int dos_errno (void)
|
|
{
|
|
DWORD e = GetLastError ();
|
|
|
|
//write_log (_T("ec=%d\n"), e);
|
|
switch (e) {
|
|
case ERROR_NOT_ENOUGH_MEMORY:
|
|
case ERROR_OUTOFMEMORY:
|
|
return ERROR_NO_FREE_STORE;
|
|
|
|
case ERROR_FILE_EXISTS:
|
|
case ERROR_ALREADY_EXISTS:
|
|
return ERROR_OBJECT_EXISTS;
|
|
|
|
case ERROR_WRITE_PROTECT:
|
|
case ERROR_ACCESS_DENIED:
|
|
return ERROR_WRITE_PROTECTED;
|
|
|
|
case ERROR_FILE_NOT_FOUND:
|
|
case ERROR_INVALID_DRIVE:
|
|
case ERROR_INVALID_NAME:
|
|
case ERROR_PATH_NOT_FOUND:
|
|
case ERROR_NOT_READY:
|
|
case ERROR_BAD_UNIT:
|
|
case ERROR_REQUEST_ABORTED:
|
|
case ERROR_INVALID_HANDLE:
|
|
case ERROR_DEV_NOT_EXIST:
|
|
case ERROR_INVALID_PARAMETER:
|
|
case ERROR_NETNAME_DELETED:
|
|
case ERROR_NETWORK_ACCESS_DENIED:
|
|
case ERROR_BAD_NET_NAME:
|
|
case ERROR_BAD_NETPATH:
|
|
case ERROR_NETWORK_BUSY:
|
|
case ERROR_SEM_TIMEOUT:
|
|
return ERROR_OBJECT_NOT_AROUND;
|
|
|
|
case ERROR_HANDLE_DISK_FULL:
|
|
case ERROR_DISK_FULL:
|
|
return ERROR_DISK_IS_FULL;
|
|
|
|
case ERROR_SHARING_VIOLATION:
|
|
case ERROR_BUSY:
|
|
case ERROR_USER_MAPPED_FILE:
|
|
return ERROR_OBJECT_IN_USE;
|
|
|
|
case ERROR_CURRENT_DIRECTORY:
|
|
return ERROR_DIRECTORY_NOT_EMPTY;
|
|
|
|
case ERROR_NEGATIVE_SEEK:
|
|
case ERROR_SEEK_ON_DEVICE:
|
|
return ERROR_SEEK_ERROR;
|
|
|
|
default:
|
|
write_log (_T("Unimplemented error %d\n"), e);
|
|
/* most common */
|
|
return ERROR_OBJECT_NOT_AROUND;
|
|
}
|
|
}
|
|
|
|
int my_getvolumeinfo (const TCHAR *root)
|
|
{
|
|
DWORD v, err;
|
|
int ret = 0;
|
|
TCHAR volume[MAX_DPATH];
|
|
|
|
v = GetFileAttributesSafe (root);
|
|
err = GetLastError ();
|
|
if (v == INVALID_FILE_ATTRIBUTES)
|
|
return -1;
|
|
if (!(v & FILE_ATTRIBUTE_DIRECTORY))
|
|
return -2;
|
|
/*
|
|
if (v & FILE_ATTRIBUTE_READONLY)
|
|
ret |= MYVOLUMEINFO_READONLY;
|
|
*/
|
|
if (GetVolumePathName (root, volume, sizeof (volume) / sizeof(TCHAR))) {
|
|
TCHAR fsname[MAX_DPATH];
|
|
DWORD comlen;
|
|
DWORD flags;
|
|
if (GetVolumeInformation (volume, NULL, 0, NULL, &comlen, &flags, fsname, sizeof fsname / sizeof (TCHAR))) {
|
|
//write_log (_T("Volume %s FS=%s maxlen=%d flags=%08X\n"), volume, fsname, comlen, flags);
|
|
if (flags & FILE_NAMED_STREAMS)
|
|
ret |= MYVOLUMEINFO_STREAMS;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
FILE *my_opentext (const TCHAR *name)
|
|
{
|
|
FILE *f;
|
|
uae_u8 tmp[4];
|
|
size_t v;
|
|
|
|
f = _tfopen (name, _T("rb"));
|
|
if (!f)
|
|
return NULL;
|
|
v = fread (tmp, 1, sizeof tmp, f);
|
|
fclose (f);
|
|
if (v == 4) {
|
|
if (tmp[0] == 0xef && tmp[1] == 0xbb && tmp[2] == 0xbf)
|
|
return _tfopen (name, _T("r, ccs=UTF-8"));
|
|
if (tmp[0] == 0xff && tmp[1] == 0xfe)
|
|
return _tfopen (name, _T("r, ccs=UTF-16LE"));
|
|
}
|
|
return _tfopen (name, _T("r"));
|
|
}
|
|
|
|
typedef BOOL (CALLBACK* GETVOLUMEINFORMATIONBYHANDLEW)
|
|
(_In_ HANDLE hFile,
|
|
LPWSTR lpVolumeNameBuffer,
|
|
DWORD nVolumeNameSize,
|
|
LPDWORD lpVolumeSerialNumber,
|
|
LPDWORD lpMaximumComponentLength,
|
|
LPDWORD lpFileSystemFlags,
|
|
LPWSTR lpFileSystemNameBuffer,
|
|
DWORD nFileSystemNameSize);
|
|
|
|
|
|
static bool isfat (HANDLE h)
|
|
{
|
|
TCHAR fsname[MAX_DPATH];
|
|
DWORD comlen;
|
|
DWORD flags;
|
|
GETVOLUMEINFORMATIONBYHANDLEW pGetVolumeInformationByHandleW;
|
|
|
|
pGetVolumeInformationByHandleW = (GETVOLUMEINFORMATIONBYHANDLEW)GetProcAddress(
|
|
GetModuleHandle (_T("kernel32.dll")), "GetVolumeInformationByHandleW");
|
|
if (!pGetVolumeInformationByHandleW)
|
|
return false;
|
|
if (pGetVolumeInformationByHandleW (h, NULL, NULL, NULL, &comlen, &flags, fsname, sizeof fsname / sizeof (TCHAR))) {
|
|
if (!_tcsncmp (fsname, _T("FAT"), 3)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool my_stat (const TCHAR *name, struct mystat *statbuf)
|
|
{
|
|
DWORD attr, ok;
|
|
FILETIME ft, lft;
|
|
HANDLE h;
|
|
BY_HANDLE_FILE_INFORMATION fi;
|
|
const TCHAR *namep;
|
|
bool fat;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
|
|
// FILE_FLAG_BACKUP_SEMANTICS = can also "open" directories
|
|
h = CreateFile (namep, 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return false;
|
|
fat = isfat (h);
|
|
|
|
ok = GetFileInformationByHandle (h, &fi);
|
|
CloseHandle (h);
|
|
|
|
attr = 0;
|
|
ft.dwHighDateTime = ft.dwLowDateTime = 0;
|
|
if (ok) {
|
|
attr = fi.dwFileAttributes;
|
|
if (fat) {
|
|
// fat lastwritetime only has 2 second resolution
|
|
// fat creationtime has 10ms resolution
|
|
// use creationtime if creationtime is inside lastwritetime 2s resolution
|
|
ULARGE_INTEGER ct, wt;
|
|
ct.HighPart = fi.ftCreationTime.dwHighDateTime;
|
|
ct.LowPart = fi.ftCreationTime.dwLowDateTime;
|
|
wt.HighPart = fi.ftLastWriteTime.dwHighDateTime;
|
|
wt.LowPart = fi.ftLastWriteTime.dwLowDateTime;
|
|
uae_u64 ctsec = ct.QuadPart / 10000000;
|
|
uae_u64 wtsec = wt.QuadPart / 10000000;
|
|
if (wtsec == ctsec || wtsec + 1 == ctsec) {
|
|
ft = fi.ftCreationTime;
|
|
} else {
|
|
ft = fi.ftLastWriteTime;
|
|
}
|
|
} else {
|
|
ft = fi.ftLastWriteTime;
|
|
}
|
|
statbuf->size = ((uae_u64)fi.nFileSizeHigh << 32) | fi.nFileSizeLow;
|
|
} else {
|
|
write_log (_T("GetFileInformationByHandle(%s) failed: %d\n"), namep, GetLastError ());
|
|
return false;
|
|
}
|
|
|
|
statbuf->mode = (attr & FILE_ATTRIBUTE_READONLY) ? FILEFLAG_READ : FILEFLAG_READ | FILEFLAG_WRITE;
|
|
if (attr & FILE_ATTRIBUTE_ARCHIVE)
|
|
statbuf->mode |= FILEFLAG_ARCHIVE;
|
|
if (attr & FILE_ATTRIBUTE_DIRECTORY)
|
|
statbuf->mode |= FILEFLAG_DIR;
|
|
|
|
FileTimeToLocalFileTime (&ft,&lft);
|
|
uae_u64 t = (*(__int64 *)&lft-((__int64)(369*365+89)*(__int64)(24*60*60)*(__int64)10000000));
|
|
statbuf->mtime.tv_sec = t / 10000000;
|
|
statbuf->mtime.tv_usec = (t / 10) % 1000000;
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
bool my_chmod (const TCHAR *name, uae_u32 mode)
|
|
{
|
|
DWORD attr = FILE_ATTRIBUTE_NORMAL;
|
|
if (!(mode & FILEFLAG_WRITE))
|
|
attr |= FILE_ATTRIBUTE_READONLY;
|
|
if (mode & FILEFLAG_ARCHIVE)
|
|
attr |= FILE_ATTRIBUTE_ARCHIVE;
|
|
if (SetFileAttributesSafe (name, attr))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
static void tmToSystemTime (struct tm *tmtime, LPSYSTEMTIME systime)
|
|
{
|
|
if (tmtime == NULL) {
|
|
GetSystemTime (systime);
|
|
} else {
|
|
systime->wDay = tmtime->tm_mday;
|
|
systime->wDayOfWeek = tmtime->tm_wday;
|
|
systime->wMonth = tmtime->tm_mon + 1;
|
|
systime->wYear = tmtime->tm_year + 1900;
|
|
systime->wHour = tmtime->tm_hour;
|
|
systime->wMinute = tmtime->tm_min;
|
|
systime->wSecond = tmtime->tm_sec;
|
|
systime->wMilliseconds = 0;
|
|
}
|
|
}
|
|
|
|
static int setfiletime (const TCHAR *name, int days, int minute, int tick, int tolocal)
|
|
{
|
|
FILETIME LocalFileTime, FileTime;
|
|
HANDLE hFile;
|
|
const TCHAR *namep;
|
|
TCHAR path[MAX_DPATH];
|
|
|
|
if (currprefs.win32_filesystem_mangle_reserved_names == false) {
|
|
_tcscpy (path, PATHPREFIX);
|
|
_tcscat (path, name);
|
|
namep = path;
|
|
} else {
|
|
namep = name;
|
|
}
|
|
|
|
if ((hFile = CreateFile(namep, FILE_WRITE_ATTRIBUTES, FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)) == INVALID_HANDLE_VALUE)
|
|
return 0;
|
|
|
|
for (;;) {
|
|
ULARGE_INTEGER lft;
|
|
|
|
lft.QuadPart = (((uae_u64)(377*365+91+days)*(uae_u64)1440+(uae_u64)minute)*(uae_u64)(60*50)+(uae_u64)tick)*(uae_u64)200000;
|
|
LocalFileTime.dwHighDateTime = lft.HighPart;
|
|
LocalFileTime.dwLowDateTime = lft.LowPart;
|
|
if (tolocal) {
|
|
if (!LocalFileTimeToFileTime (&LocalFileTime, &FileTime))
|
|
FileTime = LocalFileTime;
|
|
} else {
|
|
FileTime = LocalFileTime;
|
|
}
|
|
if (!SetFileTime (hFile, &FileTime, &FileTime, &FileTime)) {
|
|
if (days > 47846) { // > 2108-12-31 (fat limit)
|
|
days = 47846;
|
|
continue;
|
|
}
|
|
if (days < 730) { // < 1980-01-01 (fat limit)
|
|
days = 730;
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
CloseHandle (hFile);
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool my_utime (const TCHAR *name, struct mytimeval *tv)
|
|
{
|
|
int result = -1, tolocal;
|
|
int days, mins, ticks;
|
|
struct mytimeval tv2;
|
|
|
|
if (!tv) {
|
|
struct timeb time;
|
|
ftime (&time);
|
|
tv2.tv_sec = time.time;
|
|
tv2.tv_usec = time.millitm * 1000;
|
|
tolocal = 0;
|
|
} else {
|
|
tv2.tv_sec = tv->tv_sec;
|
|
tv2.tv_usec = tv->tv_usec;
|
|
tolocal = 1;
|
|
}
|
|
timeval_to_amiga (&tv2, &days, &mins, &ticks, 50);
|
|
if (setfiletime (name, days, mins, ticks, tolocal))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool my_createsoftlink(const TCHAR *path, const TCHAR *target)
|
|
{
|
|
return CreateSymbolicLink(path, target, my_existsdir (target) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0) != 0;
|
|
}
|
|
|
|
bool my_issamepath(const TCHAR *path1, const TCHAR *path2)
|
|
{
|
|
TCHAR path1o[MAX_DPATH], path2o[MAX_DPATH];
|
|
my_canonicalize_path(path1, path1o, sizeof path1o / sizeof(TCHAR));
|
|
my_canonicalize_path(path2, path2o, sizeof path2o / sizeof(TCHAR));
|
|
if (!_tcsicmp(path1o, path2o))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
void my_canonicalize_path(const TCHAR *path, TCHAR *out, int size)
|
|
{
|
|
TCHAR tmp[MAX_DPATH];
|
|
int v;
|
|
|
|
// don't attempt to validate and canonicalize invalid or fake paths
|
|
if (path[0] == ':' || path[0] == 0 || _tcscmp(path, _T("\\")) == 0 || _tcscmp(path, _T("/")) == 0) {
|
|
_tcsncpy(out, path, size);
|
|
out[size - 1] = 0;
|
|
return;
|
|
}
|
|
// skip network paths, prevent common few seconds delay.
|
|
if (path[0] == '\\' && path[1] == '\\') {
|
|
_tcsncpy(out, path, size);
|
|
out[size - 1] = 0;
|
|
return;
|
|
}
|
|
v = GetLongPathName (path, tmp, sizeof tmp / sizeof (TCHAR));
|
|
if (v > sizeof tmp / sizeof (TCHAR)) {
|
|
_tcsncpy (out, path, size);
|
|
out[size - 1] = 0;
|
|
return;
|
|
}
|
|
if (!v)
|
|
_tcscpy(tmp, path);
|
|
GetFullPathName(tmp, size, out, NULL);
|
|
}
|
|
|
|
int my_issamevolume(const TCHAR *path1, const TCHAR *path2, TCHAR *path)
|
|
{
|
|
TCHAR p1[MAX_DPATH];
|
|
TCHAR p2[MAX_DPATH];
|
|
int cnt;
|
|
size_t len;
|
|
|
|
my_canonicalize_path(path1, p1, sizeof p1 / sizeof (TCHAR));
|
|
my_canonicalize_path(path2, p2, sizeof p2 / sizeof (TCHAR));
|
|
len = _tcslen (p1);
|
|
if (len > _tcslen (p2))
|
|
len = _tcslen (p2);
|
|
if (_tcsnicmp (p1, p2, len))
|
|
return 0;
|
|
_tcscpy (path, p2 + len);
|
|
cnt = 0;
|
|
for (int i = 0; i < _tcslen (path); i++) {
|
|
if (path[i] == '\\' || path[i] == '/') {
|
|
path[i] = '/';
|
|
cnt++;
|
|
}
|
|
}
|
|
write_log (_T("'%s' (%s) matched with '%s' (%s), extra = '%s'\n"), path1, p1, path2, p2, path);
|
|
return cnt;
|
|
}
|
|
|
|
typedef struct _REPARSE_DATA_BUFFER {
|
|
ULONG ReparseTag;
|
|
USHORT ReparseDataLength;
|
|
USHORT Reserved;
|
|
union {
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
ULONG Flags;
|
|
WCHAR PathBuffer[1];
|
|
} SymbolicLinkReparseBuffer;
|
|
struct {
|
|
USHORT SubstituteNameOffset;
|
|
USHORT SubstituteNameLength;
|
|
USHORT PrintNameOffset;
|
|
USHORT PrintNameLength;
|
|
WCHAR PathBuffer[1];
|
|
} MountPointReparseBuffer;
|
|
struct {
|
|
UCHAR DataBuffer[1];
|
|
} GenericReparseBuffer;
|
|
};
|
|
} REPARSE_DATA_BUFFER, *PREPARSE_DATA_BUFFER;
|
|
|
|
static int my_resolvessymboliclink2(TCHAR *linkfile, int size)
|
|
{
|
|
WIN32_FIND_DATA fd;
|
|
HANDLE h;
|
|
int ret = -1;
|
|
DWORD returnedDataSize;
|
|
uae_u8 tmp[MAX_DPATH * 2];
|
|
TCHAR tmp2[MAX_DPATH];
|
|
|
|
h = FindFirstFile (linkfile, &fd);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return -1;
|
|
FindClose(h);
|
|
if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) || fd.dwReserved0 != IO_REPARSE_TAG_SYMLINK)
|
|
return -1;
|
|
h = CreateFile (linkfile, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL,
|
|
OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_REPARSE_POINT | FILE_FLAG_OPEN_REPARSE_POINT , NULL);
|
|
if (h == INVALID_HANDLE_VALUE)
|
|
return 0;
|
|
if (DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0, tmp, sizeof tmp, &returnedDataSize, NULL)) {
|
|
REPARSE_DATA_BUFFER *rdb = (REPARSE_DATA_BUFFER*)tmp;
|
|
if (size > 0) {
|
|
if (rdb->SymbolicLinkReparseBuffer.Flags & 1) { // SYMLINK_FLAG_RELATIVE
|
|
_tcscpy (tmp2, linkfile);
|
|
PathRemoveFileSpec (tmp2);
|
|
_tcscat (tmp2, _T("\\"));
|
|
TCHAR *p = tmp2 + _tcslen (tmp2);
|
|
memcpy (p,
|
|
(uae_u8*)rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset,
|
|
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
|
p[rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / 2] = 0;
|
|
} else {
|
|
memcpy (tmp2,
|
|
(uae_u8*)rdb->SymbolicLinkReparseBuffer.PathBuffer + rdb->SymbolicLinkReparseBuffer.SubstituteNameOffset,
|
|
rdb->SymbolicLinkReparseBuffer.SubstituteNameLength);
|
|
tmp2[rdb->SymbolicLinkReparseBuffer.SubstituteNameLength / 2] = 0;
|
|
}
|
|
if (!_tcsnicmp (tmp2, _T("\\??\\"), 4)) {
|
|
memmove (tmp2, tmp2 + 4, (_tcslen (tmp2 + 4) + 1) * sizeof (TCHAR));
|
|
}
|
|
my_canonicalize_path (tmp2, linkfile, size);
|
|
}
|
|
ret = 1;
|
|
}
|
|
CloseHandle(h);
|
|
return ret;
|
|
}
|
|
bool my_resolvessymboliclink(TCHAR *linkfile, int size)
|
|
{
|
|
return my_resolvessymboliclink2(linkfile, size) > 0;
|
|
}
|
|
|
|
// http://msdn.microsoft.com/en-us/library/aa969393.aspx
|
|
bool my_resolveshortcut(TCHAR *linkfile, int size)
|
|
{
|
|
bool ok = false;
|
|
HRESULT hres;
|
|
IShellLink* psl;
|
|
WCHAR szGotPath[MAX_DPATH];
|
|
WCHAR szDescription[MAX_DPATH];
|
|
WIN32_FIND_DATA wfd;
|
|
|
|
const TCHAR *ext = _tcsrchr (linkfile, '.');
|
|
if (!ext || _tcsicmp (ext, _T(".lnk")) != 0)
|
|
return false;
|
|
|
|
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
|
|
// has already been called.
|
|
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IPersistFile* ppf;
|
|
|
|
// Get a pointer to the IPersistFile interface.
|
|
hres = psl->QueryInterface(IID_IPersistFile, (void**)&ppf);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Add code here to check return value from MultiByteWideChar
|
|
// for success.
|
|
|
|
// Load the shortcut.
|
|
hres = ppf->Load(linkfile, STGM_READ);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Resolve the link.
|
|
hres = psl->Resolve(NULL, SLR_NO_UI);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Get the path to the link target.
|
|
hres = psl->GetPath(szGotPath, MAX_DPATH, &wfd, SLGP_SHORTPATH);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Get the description of the target.
|
|
hres = psl->GetDescription(szDescription, MAX_DPATH);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
if (size > 0)
|
|
my_canonicalize_path (szGotPath, linkfile, size);
|
|
ok = SUCCEEDED(hres);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Release the pointer to the IPersistFile interface.
|
|
ppf->Release();
|
|
}
|
|
|
|
// Release the pointer to the IShellLink interface.
|
|
psl->Release();
|
|
}
|
|
return ok;
|
|
}
|
|
|
|
// http://msdn.microsoft.com/en-us/library/aa969393.aspx
|
|
bool my_createshortcut(const TCHAR *source, const TCHAR *target, const TCHAR *description)
|
|
{
|
|
HRESULT hres;
|
|
IShellLink *psl;
|
|
|
|
// Get a pointer to the IShellLink interface. It is assumed that CoInitialize
|
|
// has already been called.
|
|
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (LPVOID*)&psl);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
IPersistFile *ppf;
|
|
|
|
// Set the path to the shortcut target and add the description.
|
|
TCHAR target2[MAX_DPATH];
|
|
my_canonicalize_path(target, target2, sizeof(target2) / sizeof(TCHAR));
|
|
psl->SetPath(target2);
|
|
psl->SetDescription(description);
|
|
|
|
// Query IShellLink for the IPersistFile interface, used for saving the
|
|
// shortcut in persistent storage.
|
|
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID*)&ppf);
|
|
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
// Save the link by calling IPersistFile::Save.
|
|
TCHAR tmp[MAX_DPATH];
|
|
my_canonicalize_path(source, tmp, sizeof(tmp) / sizeof(TCHAR));
|
|
const TCHAR *ext = _tcsrchr (tmp, '.');
|
|
if (!ext || _tcsicmp(ext, _T(".lnk")) != 0) {
|
|
_tcscat(tmp, _T(".lnk"));
|
|
}
|
|
hres = ppf->Save(tmp, TRUE);
|
|
ppf->Release();
|
|
}
|
|
psl->Release();
|
|
}
|
|
return SUCCEEDED(hres);
|
|
}
|
|
|
|
bool my_resolvesoftlink(TCHAR *linkfile, int size, bool linkonly)
|
|
{
|
|
if (!linkonly) {
|
|
int v = my_resolvessymboliclink2(linkfile, size);
|
|
if (v > 0)
|
|
return true;
|
|
if (v == 0)
|
|
return false;
|
|
}
|
|
if (my_resolveshortcut(linkfile,size))
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
const TCHAR *my_getfilepart(const TCHAR *filename)
|
|
{
|
|
const TCHAR *p;
|
|
|
|
p = _tcsrchr(filename, '\\');
|
|
if (p)
|
|
return p + 1;
|
|
p = _tcsrchr(filename, '/');
|
|
if (p)
|
|
return p + 1;
|
|
return filename;
|
|
}
|