diff --git a/docs/TODO.txt b/docs/TODO.txt index 029ef69..e0ce48a 100644 --- a/docs/TODO.txt +++ b/docs/TODO.txt @@ -62,8 +62,6 @@ Other stuff I thought of... - Doxygen replacement? (manpages suck.) - Fix coding standards to match. - See if we can ditch some #include lines... -- We lost Vista symlink support when removing isSymLink(). Pull it back from - revision control. - PHYSFS_exists() fails if you mountIo with a NULL filename. We need to decide how this API should work. - ZIP64 support? diff --git a/src/platform_windows.c b/src/platform_windows.c index 0a9db8d..5c6b01c 100644 --- a/src/platform_windows.c +++ b/src/platform_windows.c @@ -42,6 +42,7 @@ #define PHYSFS_INVALID_FILE_ATTRIBUTES 0xFFFFFFFF /* Not defined before the Vista SDK. */ +#define PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT 0x400 #define PHYSFS_IO_REPARSE_TAG_SYMLINK 0xA000000C @@ -866,17 +867,47 @@ static PHYSFS_sint64 FileTimeToPhysfsTime(const FILETIME *ft) } /* FileTimeToPhysfsTime */ +/* check for symlinks. These exist in NTFS 3.1 (WinXP), even though + they aren't really available to userspace before Vista. I wonder + what would happen if you put an NTFS disk with a symlink on it + into an XP machine, though; would this flag get set? + NTFS symlinks are a form of "reparse point" (junction, volume mount, + etc), so if the REPARSE_POINT attribute is set, check for the symlink + tag thereafter. This assumes you already read in the file attributes. */ +static int isSymlink(const WCHAR *wpath, const DWORD attr) +{ + WIN32_FIND_DATAW w32dw; + HANDLE h; + + if ((attr & PHYSFS_FILE_ATTRIBUTE_REPARSE_POINT) == 0) + return 0; /* not a reparse point? Definitely not a symlink. */ + + h = FindFirstFileW(wpath, &w32dw); + if (h == INVALID_HANDLE_VALUE) + return 0; /* ...maybe the file just vanished...? */ + + FindClose(h); + return (w32dw.dwReserved == PHYSFS_IO_REPARSE_TAG_SYMLINK); +} /* isSymlink */ + + int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st) { WIN32_FILE_ATTRIBUTE_DATA winstat; WCHAR *wstr = NULL; DWORD err = 0; BOOL rc = 0; + int issymlink = 0; UTF8_TO_UNICODE_STACK(wstr, filename); BAIL_IF(!wstr, PHYSFS_ERR_OUT_OF_MEMORY, 0); rc = GetFileAttributesExW(wstr, GetFileExInfoStandard, &winstat); - err = (!rc) ? GetLastError() : 0; + + if (!rc) + err = GetLastError(); + else /* check for symlink while wstr is still available */ + issymlink = isSymlink(wstr, winstat.dwFileAttributes); + __PHYSFS_smallFree(wstr); BAIL_IF(!rc, errcodeFromWinApiError(err), 0); @@ -884,22 +915,25 @@ int __PHYSFS_platformStat(const char *filename, PHYSFS_Stat *st) st->accesstime = FileTimeToPhysfsTime(&winstat.ftLastAccessTime); st->createtime = FileTimeToPhysfsTime(&winstat.ftCreationTime); - if(winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + if (issymlink) { - st->filetype = PHYSFS_FILETYPE_DIRECTORY; + st->filetype = PHYSFS_FILETYPE_SYMLINK; st->filesize = 0; } /* if */ - else if(winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE)) + else if (winstat.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + st->filetype = PHYSFS_FILETYPE_DIRECTORY; + st->filesize = 0; + } /* else if */ + + else if (winstat.dwFileAttributes & (FILE_ATTRIBUTE_OFFLINE | FILE_ATTRIBUTE_DEVICE)) { - /* !!! FIXME: what are reparse points? */ st->filetype = PHYSFS_FILETYPE_OTHER; /* !!! FIXME: don't rely on this */ st->filesize = 0; } /* else if */ - /* !!! FIXME: check for symlinks on Vista. */ - else { st->filetype = PHYSFS_FILETYPE_REGULAR;