diff options
Diffstat (limited to 'winsup/cygwin/path.cc')
-rw-r--r-- | winsup/cygwin/path.cc | 374 |
1 files changed, 198 insertions, 176 deletions
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 108ac67e7..6720f1d6b 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -1947,6 +1947,32 @@ mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit) posix_path must have sufficient space (i.e. CYG_MAX_PATH bytes). If keep_rel_p is non-zero, relative paths stay that way. */ +/* TODO: Change conv_to_posix_path to work with native paths. */ + +/* src_path is a wide Win32 path. */ +int +mount_info::conv_to_posix_path (PWCHAR src_path, char *posix_path, + int keep_rel_p) +{ + bool changed = false; + if (!wcsncmp (src_path, L"\\\\?\\", 4)) + { + src_path += 4; + if (!wcsncmp (src_path, L"UNC\\", 4)) + { + src_path += 2; + src_path[0] = L'\\'; + changed = true; + } + } + char buf[PATH_MAX]; + sys_wcstombs (buf, PATH_MAX, src_path, 0); + int ret = conv_to_posix_path (buf, posix_path, keep_rel_p); + if (changed) + src_path[0] = L'C'; + return ret; +} + int mount_info::conv_to_posix_path (const char *src_path, char *posix_path, int keep_rel_p) @@ -3357,8 +3383,7 @@ symlink_info::posixify (char *srcbuf) { char cvtbuf[SYMLINK_MAX + 1]; - strncpy (cvtbuf, cygheap->cwd.win32, 2); - strcpy (cvtbuf + 2, srcbuf); + stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf); mount_table->conv_to_posix_path (cvtbuf, contents, 0); } } @@ -3868,18 +3893,6 @@ hash_path_name (__ino64_t hash, PUNICODE_STRING name) if (name->Length == 0) return hash; - /* Fill out the hashed path name with the current working directory if - this is not an absolute path and there is no pre-specified hash value. - Otherwise the inodes same will differ depending on whether a file is - referenced with an absolute value or relatively. */ - if (!hash && !isabspath_u (name)) - { - hash = cygheap->cwd.get_hash (); - if (name->Length == sizeof (WCHAR) && name->Buffer[0] == L'.') - return hash; - hash = L'\\' + (hash << 6) + (hash << 16) - hash; - } - /* Build up hash. Name is already normalized */ USHORT len = name->Length / sizeof (WCHAR); for (USHORT idx = 0; idx < len; ++idx) @@ -3954,19 +3967,10 @@ chdir (const char *in_dir) int res = -1; bool doit = false; - const char *native_dir = path.get_win32 (), *posix_cwd = NULL; + const char *posix_cwd = NULL; int devn = path.get_devn (); if (!isvirtual_dev (devn)) { - /* Check to see if path translates to something like C:. - If it does, append a \ to the native directory specification to - defeat the Windows 95 (i.e. MS-DOS) tendency of returning to - the last directory visited on the given drive. */ - if (isdrive (native_dir) && !native_dir[2]) - { - path.get_win32 ()[2] = '\\'; - path.get_win32 ()[3] = '\0'; - } /* The sequence chdir("xx"); chdir(".."); must be a noop if xx is not a symlink. This is exploited by find.exe. The posix_cwd is just path.normalized_path. @@ -3988,12 +3992,12 @@ chdir (const char *in_dir) } if (!res) - res = cygheap->cwd.set (native_dir, posix_cwd, doit); + res = cygheap->cwd.set (path.get_nt_native_path (), posix_cwd, doit); /* Note that we're accessing cwd.posix without a lock here. I didn't think it was worth locking just for strace. */ - syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%s'", res, - cygheap->cwd.posix, native_dir); + syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%S'", res, + cygheap->cwd.posix, path.get_nt_native_path ()); MALLOC_CHECK; return res; } @@ -4339,37 +4343,10 @@ cygwin_split_path (const char *path, char *dir, char *file) /*****************************************************************************/ -/* Return the hash value for the current win32 value. - This is used when constructing inodes. */ -DWORD -cwdstuff::get_hash () -{ - DWORD hashnow; - cwd_lock.acquire (); - hashnow = hash; - cwd_lock.release (); - return hashnow; -} - -extern char windows_system_directory[]; - -static PRTL_USER_PROCESS_PARAMETERS _upp NO_COPY; - -static PRTL_USER_PROCESS_PARAMETERS +static inline PRTL_USER_PROCESS_PARAMETERS get_user_proc_parms () { - if (!_upp) - { - NTSTATUS stat; - PROCESS_BASIC_INFORMATION pbi; - stat = NtQueryInformationProcess (GetCurrentProcess (), - ProcessBasicInformation, - &pbi, sizeof pbi, NULL); - if (!NT_SUCCESS (stat)) - api_fatal ("Can't retrieve process parameters, status %p", stat); - _upp = pbi.PebBaseAddress->ProcessParameters; - } - return _upp; + return NtCurrentTeb ()->Peb->ProcessParameters; } /* Initialize cygcwd 'muto' for serializing access to cwd info. */ @@ -4377,146 +4354,189 @@ void cwdstuff::init () { cwd_lock.init ("cwd_lock"); - get_initial (); /* Initially re-open the cwd to allow POSIX semantics. */ - set (win32, posix, true); - cwd_lock.release (); + set (NULL, NULL, true); } -/* Get initial cwd. Should only be called once in a process tree. */ -bool -cwdstuff::get_initial () +/* Chdir and fill out the elements of a cwdstuff struct. */ +int +cwdstuff::set (PUNICODE_STRING nat_cwd, const char *posix_cwd, bool doit) { + int res = 0; + UNICODE_STRING upath; + size_t len = 0; + cwd_lock.acquire (); - if (win32) - return 1; + if (nat_cwd) + { + upath = *nat_cwd; + if (upath.Buffer[0] == L'/') /* Virtual path. Never use in PEB. */ + doit = false; + else + { + len = upath.Length / sizeof (WCHAR) - 4; + if (RtlEqualUnicodePathPrefix (&upath, L"\\??\\UNC\\", TRUE)) + len -= 2; + } + } - /* Leaves cwd lock unreleased, if success */ - return !set (NULL, NULL, false); -} + if (doit) + { + /* We utilize the user parameter block. The directory is + stored manually there. Why the hassle? -/* Chdir and fill out the elements of a cwdstuff struct. - It is assumed that the lock for the cwd is acquired if - win32_cwd == NULL. */ -int -cwdstuff::set (const char *win32_cwd, const char *posix_cwd, bool doit) -{ - char pathbuf[2 * CYG_MAX_PATH]; - int res = -1; + - SetCurrentDirectory fails for directories with strict + permissions even for processes with the SE_BACKUP_NAME + privilege enabled. The reason is apparently that + SetCurrentDirectory calls NtOpenFile without the + FILE_OPEN_FOR_BACKUP_INTENT flag set. - if (win32_cwd) - { - cwd_lock.acquire (); - if (doit) + - Unlinking a cwd fails because SetCurrentDirectory seems to + open directories so that deleting the directory is disallowed. + The below code opens with *all* sharing flags set. */ + HANDLE h; + NTSTATUS status; + IO_STATUS_BLOCK io; + OBJECT_ATTRIBUTES attr; + PHANDLE phdl; + + RtlAcquirePebLock (); + phdl = &get_user_proc_parms ()->CurrentDirectoryHandle; + if (!nat_cwd) /* On init, just reopen CWD with desired access flags. */ + RtlInitUnicodeString (&upath, L""); + else { - /* We utilize the user parameter block. The directory is - stored manually there. Why the hassle? - - - SetCurrentDirectory fails for directories with strict - permissions even for processes with the SE_BACKUP_NAME - privilege enabled. The reason is apparently that - SetCurrentDirectory calls NtOpenFile without the - FILE_OPEN_FOR_BACKUP_INTENT flag set. - - - Unlinking a cwd fails because SetCurrentDirectory seems to - open directories so that deleting the directory is disallowed. - The below code opens with *all* sharing flags set. */ - HANDLE h; - DWORD attr = GetFileAttributes (win32_cwd); - if (attr == INVALID_FILE_ATTRIBUTES) - { - set_errno (ENOENT); - goto out; - } - if (!(attr & FILE_ATTRIBUTE_DIRECTORY)) - { - set_errno (ENOTDIR); - goto out; - } - h = CreateFile (win32_cwd, FILE_TRAVERSE, FILE_SHARE_VALID_FLAGS, - &sec_none, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, - NULL); - if (h == INVALID_HANDLE_VALUE) - { - __seterrno (); - goto out; - } - ULONG len = strlen (win32_cwd); - ANSI_STRING as = {len, len + 2, (PCHAR) alloca (len + 2)}; - strcpy (as.Buffer, win32_cwd); - if (as.Buffer[len - 1] != '\\') + /* TODO: + Check the length of the new CWD. Windows can only handle + CWDs of up to MAX_PATH length, including a trailing backslash. + If the path is longer, it's not an error condition for Cygwin, + so we don't fail. Windows on the other hand has a problem now. + For now, we just don't store the path in the PEB and proceed as + usual. */ + if (len > MAX_PATH - (nat_cwd->Buffer[len - 1] == L'\\' ? 1 : 2)) + goto skip_peb_storing; + } + InitializeObjectAttributes (&attr, &upath, + OBJ_CASE_INSENSITIVE | OBJ_INHERIT, + nat_cwd ? NULL : *phdl, NULL); + status = NtOpenFile (&h, SYNCHRONIZE | FILE_TRAVERSE, &attr, &io, + FILE_SHARE_VALID_FLAGS, + FILE_DIRECTORY_FILE + | FILE_SYNCHRONOUS_IO_NONALERT + | FILE_OPEN_FOR_BACKUP_INTENT); + if (!NT_SUCCESS (status)) + { + RtlReleasePebLock (); + __seterrno_from_nt_status (status); + res = -1; + goto out; + } + + if (nat_cwd) /* No need to set path on init. */ + { + /* Convert to a Win32 path. */ + upath.Buffer += upath.Length / sizeof (WCHAR) - len; + if (upath.Buffer[1] == L'\\') /* UNC path */ + upath.Buffer[0] = L'\\'; + upath.Length = len * sizeof (WCHAR); + /* Append backslash if necessary. */ + if (upath.Buffer[len - 1] != L'\\') { - strcpy (as.Buffer + len, "\\"); - ++as.Length; + upath.Buffer[len] = L'\\'; + upath.Length += sizeof (WCHAR); } - RtlAcquirePebLock (); - if (current_codepage == ansi_cp) - RtlAnsiStringToUnicodeString ( - &get_user_proc_parms ()->CurrentDirectoryName, - &as, FALSE); - else - RtlOemStringToUnicodeString ( - &get_user_proc_parms ()->CurrentDirectoryName, - &as, FALSE); - PHANDLE phdl = &get_user_proc_parms ()->CurrentDirectoryHandle; - if (*phdl) - CloseHandle (*phdl); - *phdl = h; - RtlReleasePebLock (); + RtlCopyUnicodeString (&get_user_proc_parms ()->CurrentDirectoryName, + &upath); } + NtClose (*phdl); + /* Workaround a problem in Vista which fails in subsequent calls to + CreateFile with ERROR_INVALID_HANDLE if the handle in + CurrentDirectoryHandle changes without calling SetCurrentDirectory, + and the filename given to CreateFile is a relative path. It looks + like Vista stores a copy of the CWD handle in some other undocumented + place. The NtClose/DuplicateHandle reuses the original handle for + the copy of the new handle and the next CreateFile works. + Note that this is not thread-safe (yet?) */ + if (DuplicateHandle (GetCurrentProcess (), h, GetCurrentProcess (), phdl, + 0, TRUE, DUPLICATE_SAME_ACCESS)) + NtClose (h); + else + *phdl = h; + +skip_peb_storing: + + RtlReleasePebLock (); } - /* If there is no win32 path or it has the form c:xxx, get the value */ - if (!win32_cwd || (isdrive (win32_cwd) && win32_cwd[2] != '\\')) + + if (nat_cwd || !win32.Buffer) { - int i; - DWORD len, dlen; - for (i = 0, dlen = CYG_MAX_PATH/3; i < 2; i++, dlen = len) + /* If there is no win32 path */ + if (!nat_cwd) { - win32 = (char *) crealloc (win32, dlen); - if ((len = GetCurrentDirectoryA (dlen, win32)) < dlen) - break; + PUNICODE_STRING pdir; + + RtlAcquirePebLock (); + pdir = &get_user_proc_parms ()->CurrentDirectoryName; + RtlInitEmptyUnicodeString (&win32, + (PWCHAR) crealloc (win32.Buffer, + pdir->Length + 2), + pdir->Length + 2); + RtlCopyUnicodeString (&win32, pdir); + RtlReleasePebLock (); + /* Remove trailing slash. */ + if (win32.Length > 3 * sizeof (WCHAR)) + win32.Length -= sizeof (WCHAR); + posix_cwd = NULL; } - if (len == 0) + else { - __seterrno (); - debug_printf ("GetCurrentDirectory, %E"); - win32_cwd = pathbuf; /* Force lock release */ - goto out; + if (upath.Buffer[0] == L'/') /* Virtual path, don't mangle. */ + ; + else if (!doit) + { + /* Convert to a Win32 path. */ + upath.Buffer += upath.Length / sizeof (WCHAR) - len; + if (upath.Buffer[1] == L'\\') /* UNC path */ + upath.Buffer[0] = L'\\'; + upath.Length = len * sizeof (WCHAR); + } + else if (upath.Length > 3 * sizeof (WCHAR)) + upath.Length -= sizeof (WCHAR); /* Strip trailing backslash */ + RtlInitEmptyUnicodeString (&win32, + (PWCHAR) crealloc (win32.Buffer, + upath.Length + 2), + upath.Length + 2); + RtlCopyUnicodeString (&win32, &upath); } - posix_cwd = NULL; - } - else - { - win32 = (char *) crealloc (win32, strlen (win32_cwd) + 1); - strcpy (win32, win32_cwd); - } - if (win32[1] == ':') - drive_length = 2; - else if (win32[1] == '\\') - { - char * ptr = strechr (win32 + 2, '\\'); - if (*ptr) - ptr = strechr (ptr + 1, '\\'); - drive_length = ptr - win32; - } - else - drive_length = 0; + /* Make sure it's NUL-termniated. */ + win32.Buffer[win32.Length / sizeof (WCHAR)] = L'\0'; + if (win32.Buffer[1] == L':') + drive_length = 2; + else if (win32.Buffer[1] == L'\\') + { + PWCHAR ptr = wcschr (win32.Buffer + 2, L'\\'); + if (*ptr) + ptr = wcschr (ptr + 1, L'\\'); + if (ptr) + drive_length = ptr - win32.Buffer; + else + drive_length = win32.Length / sizeof (WCHAR); + } + else + drive_length = 0; - if (!posix_cwd) - { - mount_table->conv_to_posix_path (win32, pathbuf, 0); - posix_cwd = pathbuf; + if (!posix_cwd) + { + posix_cwd = (const char *) alloca (PATH_MAX); + mount_table->conv_to_posix_path (win32.Buffer, (char *) posix_cwd, 0); + } + posix = (char *) crealloc (posix, strlen (posix_cwd) + 1); + stpcpy (posix, posix_cwd); } - posix = (char *) crealloc (posix, strlen (posix_cwd) + 1); - strcpy (posix, posix_cwd); - hash = hash_path_name (0, win32); - - res = 0; out: - if (win32_cwd) - cwd_lock.release (); + cwd_lock.release (); return res; } @@ -4536,12 +4556,14 @@ cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen) goto out; } - if (!get_initial ()) /* Get initial cwd and set cwd lock */ - return NULL; + cwd_lock.acquire (); char *tocopy; if (!need_posix) - tocopy = win32; + { + tocopy = (char *) alloca (PATH_MAX); + sys_wcstombs (tocopy, PATH_MAX, win32.Buffer, win32.Length); + } else tocopy = posix; |