extern void
set_errno_from_GetLastError(void);
+extern bool
+win32_path_is_root_of_drive(const wchar_t *path);
+
extern int
win32_error_to_errno(DWORD err_code);
return ret;
}
-static bool
-path_is_root_of_drive(const wchar_t *path)
-{
- if (*path == L'\0')
- return false;
-
- if (!wcsncmp(path, L"\\\\?\\", 4))
- path += 4;
-
- if (*path != L'/' && *path != L'\\') {
- if (*(path + 1) == L':')
- path += 2;
- else
- return false;
- }
- while (*path == L'/' || *path == L'\\')
- path++;
- return (*path == L'\0');
-}
-
static inline DWORD
win32_mask_attributes(DWORD i_attributes)
{
* to CreateFileW() will merely open the directory that was already
* created rather than creating a new file. */
if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY) {
- if (!path_is_root_of_drive(path)) {
+ if (!win32_path_is_root_of_drive(path)) {
if (!CreateDirectoryW(path, NULL)) {
err = GetLastError();
if (err != ERROR_ALREADY_EXISTS) {
* directory, so treat that as a special case and do not set attributes.
* */
if (*creationDisposition_ret == OPEN_EXISTING &&
- !path_is_root_of_drive(path))
+ !win32_path_is_root_of_drive(path))
{
if (!SetFileAttributesW(path,
win32_mask_attributes(inode->i_attributes)))
if (stream_name_utf16) {
/* Named stream. Create a buffer that contains the UTF-16LE
- * string [./]path:stream_name_utf16. This is needed to
+ * string [.\]path:stream_name_utf16. This is needed to
* create and open the stream using CreateFileW(). I'm not
* aware of any other APIs to do this. Note: the '$DATA' suffix
- * seems to be unneeded. Additional note: a "./" prefix needs
- * to be added when the path is not absolute to avoid ambiguity
- * with drive letters. */
+ * seems to be unneeded. Additional note: a ".\" prefix needs
+ * to be added when the path is a 1-character long relative path
+ * to avoid ambiguity with drive letters. */
size_t stream_path_nchars;
size_t path_nchars;
size_t stream_name_nchars;
path_nchars = wcslen(path);
stream_name_nchars = wcslen(stream_name_utf16);
stream_path_nchars = path_nchars + 1 + stream_name_nchars;
- if (path[0] != cpu_to_le16(L'\0') &&
- path[0] != cpu_to_le16(L'/') &&
- path[0] != cpu_to_le16(L'\\') &&
- path[1] != cpu_to_le16(L':'))
- {
- prefix = L"./";
+ if (path_nchars == 1 && !is_any_path_separator(path[0])) {
+ static const wchar_t _prefix[] =
+ {L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
+ prefix = _prefix;
stream_path_nchars += 2;
} else {
prefix = L"";
if (h == INVALID_HANDLE_VALUE) {
err = GetLastError();
if (err == ERROR_ACCESS_DENIED &&
- path_is_root_of_drive(stream_path))
+ win32_path_is_root_of_drive(stream_path))
{
ret = 0;
goto out;
/* Windows doesn't let you change the timestamps of the root directory
* (at least on FAT, which is dumb but expected since FAT doesn't store
* any metadata about the root directory...) */
- if (path_is_root_of_drive(path))
+ if (win32_path_is_root_of_drive(path))
return 0;
DEBUG("Opening \"%ls\" to set timestamps", path);
if (is_named_stream) {
spath_nchars += 1 + stream_name_nchars;
colonchar = L":";
- if (path_num_chars == 1 &&
- path[0] != L'/' &&
- path[0] != L'\\')
- {
+ if (path_num_chars == 1 && !is_any_path_separator(path[0])) {
spath_nchars += 2;
static const wchar_t _relpath_prefix[] =
{L'.', OS_PREFERRED_PATH_SEPARATOR, L'\0'};
#include "wimlib/assert.h"
#include "wimlib/error.h"
+#include "wimlib/util.h"
#ifdef ENABLE_ERROR_MESSAGES
void
errno = win32_error_to_errno(GetLastError());
}
+/* Given a Windows-style path, return the number of characters of the prefix
+ * that specify the path to the root directory of a drive, or return 0 if the
+ * drive is relative (or at least on the current drive, in the case of
+ * absolute-but-not-really-absolute paths like \Windows\System32) */
+static size_t
+win32_path_drive_spec_len(const wchar_t *path)
+{
+ size_t n = 0;
+
+ if (!wcsncmp(path, L"\\\\?\\", 4)) {
+ /* \\?\-prefixed path. Check for following drive letter and
+ * path separator. */
+ if (path[4] != L'\0' && path[5] == L':' &&
+ is_any_path_separator(path[6]))
+ n = 7;
+ } else {
+ /* Not a \\?\-prefixed path. Check for an initial drive letter
+ * and path separator. */
+ if (path[0] != L'\0' && path[1] == L':' &&
+ is_any_path_separator(path[2]))
+ n = 3;
+ }
+ /* Include any additional path separators.*/
+ if (n > 0)
+ while (is_any_path_separator(path[n]))
+ n++;
+ return n;
+}
+
+bool
+win32_path_is_root_of_drive(const wchar_t *path)
+{
+ size_t drive_spec_len;
+
+ /* Explicit drive letter and path separator? */
+ drive_spec_len = win32_path_drive_spec_len(path);
+ if (drive_spec_len > 0 && path[drive_spec_len] == L'\0')
+ return true;
+
+ /* All path separators? */
+ for (const wchar_t *p = path; *p != L'\0'; p++)
+ if (!is_any_path_separator(*p))
+ return false;
+ return true;
+
+ /* XXX This function does not handle paths like "c:" where the working
+ * directory on "c:" is actually "c:\", or weird paths like "\.". But
+ * currently the capture and apply code always prefixes the paths with
+ * \\?\ anyway so this is irrelevant... */
+}
+
+
/* Given a path, which may not yet exist, get a set of flags that describe the
* features of the volume the path is on. */
int
wchar_t *volume;
BOOL bret;
DWORD vol_flags;
+ size_t drive_spec_len;
- if (path[0] != L'\0' && path[0] != L'\\' &&
- path[0] != L'/' && path[1] == L':')
- {
- /* Path starts with a drive letter; use it. */
- volume = alloca(4 * sizeof(wchar_t));
- volume[0] = path[0];
- volume[1] = path[1];
- volume[2] = L'\\';
- volume[3] = L'\0';
- } else {
+ drive_spec_len = win32_path_drive_spec_len(path);
+
+ if (drive_spec_len == 0)
+ if (path[0] != L'\0' && path[1] == L':') /* Drive-relative path? */
+ drive_spec_len = 2;
+
+ if (drive_spec_len == 0) {
/* Path does not start with a drive letter; use the volume of
* the current working directory. */
volume = NULL;
+ } else {
+ /* Path starts with a drive letter (or \\?\ followed by a drive
+ * letter); use it. */
+ volume = alloca((drive_spec_len + 2) * sizeof(wchar_t));
+ wmemcpy(volume, path, drive_spec_len);
+ /* Add trailing backslash in case this was a drive-relative
+ * path. */
+ volume[drive_spec_len] = L'\\';
+ volume[drive_spec_len + 1] = L'\0';
}
bret = GetVolumeInformationW(volume, /* lpRootPathName */
NULL, /* lpVolumeNameBuffer */