* cygheap.h (struct cwdstuff): Drop hash member. Drop get_hash,

get_initial, and fixup_after_exec declarations.
	Convert win32 to UNICODE_STRING.
	(cwdstuff::get_drive): Convert win32 path in current codepage.
	(cwdstuff::set): Take native NT path.
	* ntdll.h (struct _TEB): Typedef.
	* path.cc (mount_info::conv_to_posix_path): Add variant taking
	wide char DOS paths.
	(symlink_info::posixify): Simplify concatenating cwd and relative path.
	(hash_path_name): Drop special relative path handling.
	(chdir): Drop special "drive only" handling.  Call cwdstuff::set with
	native path.
	(cwdstuff::get_hash): Remove.
	(windows_system_directory): Remove.
	(_upp): Remove.
	(get_user_proc_parms): Make inline.  Get PEB pointer by calling
	NtCurrentTeb.
	(cwdstuff::init): Simplify.
	(cwdstuff::set): Rework to handle incoming native NT path.  Workaround
	a Vista problem with CWD handle in the user process parameter block.
	(cwdstuff::get): Simplify locking.  Accommodate type change of win32.
	* shared_info.h (mount_info): Add declaration for new conv_to_posix_path
	method.
	* strfuncs.cc (sys_wcstombs): Return correct length of created
	multi-byte string.
This commit is contained in:
Corinna Vinschen 2007-10-23 16:26:28 +00:00
parent 5b9de9d956
commit 09ecdc8504
6 changed files with 247 additions and 188 deletions

View File

@ -1,3 +1,31 @@
2007-10-22 Corinna Vinschen <corinna@vinschen.de>
* cygheap.h (struct cwdstuff): Drop hash member. Drop get_hash,
get_initial, and fixup_after_exec declarations.
Convert win32 to UNICODE_STRING.
(cwdstuff::get_drive): Convert win32 path in current codepage.
(cwdstuff::set): Take native NT path.
* ntdll.h (struct _TEB): Typedef.
* path.cc (mount_info::conv_to_posix_path): Add variant taking
wide char DOS paths.
(symlink_info::posixify): Simplify concatenating cwd and relative path.
(hash_path_name): Drop special relative path handling.
(chdir): Drop special "drive only" handling. Call cwdstuff::set with
native path.
(cwdstuff::get_hash): Remove.
(windows_system_directory): Remove.
(_upp): Remove.
(get_user_proc_parms): Make inline. Get PEB pointer by calling
NtCurrentTeb.
(cwdstuff::init): Simplify.
(cwdstuff::set): Rework to handle incoming native NT path. Workaround
a Vista problem with CWD handle in the user process parameter block.
(cwdstuff::get): Simplify locking. Accommodate type change of win32.
* shared_info.h (mount_info): Add declaration for new conv_to_posix_path
method.
* strfuncs.cc (sys_wcstombs): Return correct length of created
multi-byte string.
2007-10-19 Corinna Vinschen <corinna@vinschen.de> 2007-10-19 Corinna Vinschen <corinna@vinschen.de>
* fhandler_disk_file.cc (__DIR_mounts::eval_ino): Make fname big enough * fhandler_disk_file.cc (__DIR_mounts::eval_ino): Make fname big enough

View File

@ -222,23 +222,19 @@ class muto;
struct cwdstuff struct cwdstuff
{ {
char *posix; char *posix;
char *win32; UNICODE_STRING win32;
DWORD hash;
DWORD drive_length; DWORD drive_length;
static muto cwd_lock; static muto cwd_lock;
char *get (char *, int = 1, int = 0, unsigned = CYG_MAX_PATH); char *get (char *, int = 1, int = 0, unsigned = CYG_MAX_PATH);
DWORD get_hash ();
DWORD get_drive (char * dst) DWORD get_drive (char * dst)
{ {
get_initial (); cwd_lock.acquire ();
memcpy (dst, win32, drive_length); DWORD ret = sys_wcstombs (dst, PATH_MAX, win32.Buffer, drive_length);
cwd_lock.release (); cwd_lock.release ();
return drive_length; return ret;
} }
void init (); void init ();
void fixup_after_exec (char *, char *, DWORD); int set (PUNICODE_STRING, const char *, bool);
bool get_initial ();
int set (const char *, const char *, bool);
}; };
#ifdef DEBUGGING #ifdef DEBUGGING

View File

@ -527,6 +527,14 @@ typedef struct _PEB
ULONG SessionId; ULONG SessionId;
} PEB, *PPEB; } PEB, *PPEB;
/* Simplifed definition, just to get the PEB pointer. */
typedef struct _TEB
{
PVOID dummy[12];
PPEB Peb;
/* A lot more follows... */
} TEB, *PTEB;
typedef struct _PROCESS_BASIC_INFORMATION typedef struct _PROCESS_BASIC_INFORMATION
{ {
NTSTATUS ExitStatus; NTSTATUS ExitStatus;

View File

@ -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). posix_path must have sufficient space (i.e. CYG_MAX_PATH bytes).
If keep_rel_p is non-zero, relative paths stay that way. */ 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 int
mount_info::conv_to_posix_path (const char *src_path, char *posix_path, mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
int keep_rel_p) int keep_rel_p)
@ -3357,8 +3383,7 @@ symlink_info::posixify (char *srcbuf)
{ {
char cvtbuf[SYMLINK_MAX + 1]; char cvtbuf[SYMLINK_MAX + 1];
strncpy (cvtbuf, cygheap->cwd.win32, 2); stpcpy (cvtbuf + cygheap->cwd.get_drive (cvtbuf), srcbuf);
strcpy (cvtbuf + 2, srcbuf);
mount_table->conv_to_posix_path (cvtbuf, contents, 0); 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) if (name->Length == 0)
return hash; 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 */ /* Build up hash. Name is already normalized */
USHORT len = name->Length / sizeof (WCHAR); USHORT len = name->Length / sizeof (WCHAR);
for (USHORT idx = 0; idx < len; ++idx) for (USHORT idx = 0; idx < len; ++idx)
@ -3954,19 +3967,10 @@ chdir (const char *in_dir)
int res = -1; int res = -1;
bool doit = false; bool doit = false;
const char *native_dir = path.get_win32 (), *posix_cwd = NULL; const char *posix_cwd = NULL;
int devn = path.get_devn (); int devn = path.get_devn ();
if (!isvirtual_dev (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 /* The sequence chdir("xx"); chdir(".."); must be a noop if xx
is not a symlink. This is exploited by find.exe. is not a symlink. This is exploited by find.exe.
The posix_cwd is just path.normalized_path. The posix_cwd is just path.normalized_path.
@ -3988,12 +3992,12 @@ chdir (const char *in_dir)
} }
if (!res) 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 /* Note that we're accessing cwd.posix without a lock here. I didn't think
it was worth locking just for strace. */ it was worth locking just for strace. */
syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%s'", res, syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%S'", res,
cygheap->cwd.posix, native_dir); cygheap->cwd.posix, path.get_nt_native_path ());
MALLOC_CHECK; MALLOC_CHECK;
return res; 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. static inline PRTL_USER_PROCESS_PARAMETERS
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
get_user_proc_parms () get_user_proc_parms ()
{ {
if (!_upp) return NtCurrentTeb ()->Peb->ProcessParameters;
{
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;
} }
/* Initialize cygcwd 'muto' for serializing access to cwd info. */ /* Initialize cygcwd 'muto' for serializing access to cwd info. */
@ -4377,146 +4354,189 @@ void
cwdstuff::init () cwdstuff::init ()
{ {
cwd_lock.init ("cwd_lock"); cwd_lock.init ("cwd_lock");
get_initial ();
/* Initially re-open the cwd to allow POSIX semantics. */ /* Initially re-open the cwd to allow POSIX semantics. */
set (win32, posix, true); set (NULL, NULL, true);
cwd_lock.release ();
} }
/* Get initial cwd. Should only be called once in a process tree. */ /* Chdir and fill out the elements of a cwdstuff struct. */
bool int
cwdstuff::get_initial () 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 (); cwd_lock.acquire ();
if (win32) if (nat_cwd)
return 1;
/* Leaves cwd lock unreleased, if success */
return !set (NULL, NULL, false);
}
/* 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;
if (win32_cwd)
{ {
cwd_lock.acquire (); upath = *nat_cwd;
if (doit) if (upath.Buffer[0] == L'/') /* Virtual path. Never use in PEB. */
doit = false;
else
{ {
/* We utilize the user parameter block. The directory is len = upath.Length / sizeof (WCHAR) - 4;
stored manually there. Why the hassle? if (RtlEqualUnicodePathPrefix (&upath, L"\\??\\UNC\\", TRUE))
len -= 2;
- 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] != '\\')
{
strcpy (as.Buffer + len, "\\");
++as.Length;
}
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 ();
} }
} }
/* 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 (doit)
{ {
int i; /* We utilize the user parameter block. The directory is
DWORD len, dlen; stored manually there. Why the hassle?
for (i = 0, dlen = CYG_MAX_PATH/3; i < 2; i++, dlen = len)
- 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;
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
{ {
win32 = (char *) crealloc (win32, dlen); /* TODO:
if ((len = GetCurrentDirectoryA (dlen, win32)) < dlen) Check the length of the new CWD. Windows can only handle
break; 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;
} }
if (len == 0) 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))
{ {
__seterrno (); RtlReleasePebLock ();
debug_printf ("GetCurrentDirectory, %E"); __seterrno_from_nt_status (status);
win32_cwd = pathbuf; /* Force lock release */ res = -1;
goto out; goto out;
} }
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;
if (!posix_cwd) if (nat_cwd) /* No need to set path on init. */
{ {
mount_table->conv_to_posix_path (win32, pathbuf, 0); /* Convert to a Win32 path. */
posix_cwd = pathbuf; 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'\\')
{
upath.Buffer[len] = L'\\';
upath.Length += sizeof (WCHAR);
}
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 ();
} }
posix = (char *) crealloc (posix, strlen (posix_cwd) + 1);
strcpy (posix, posix_cwd);
hash = hash_path_name (0, win32); if (nat_cwd || !win32.Buffer)
{
/* If there is no win32 path */
if (!nat_cwd)
{
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;
}
else
{
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);
}
/* 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)
{
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);
}
res = 0;
out: out:
if (win32_cwd) cwd_lock.release ();
cwd_lock.release ();
return res; return res;
} }
@ -4536,12 +4556,14 @@ cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
goto out; goto out;
} }
if (!get_initial ()) /* Get initial cwd and set cwd lock */ cwd_lock.acquire ();
return NULL;
char *tocopy; char *tocopy;
if (!need_posix) if (!need_posix)
tocopy = win32; {
tocopy = (char *) alloca (PATH_MAX);
sys_wcstombs (tocopy, PATH_MAX, win32.Buffer, win32.Length);
}
else else
tocopy = posix; tocopy = posix;

View File

@ -82,6 +82,8 @@ class mount_info
unsigned set_flags_from_win32_path (const char *path); unsigned set_flags_from_win32_path (const char *path);
int conv_to_win32_path (const char *src_path, char *dst, device&, int conv_to_win32_path (const char *src_path, char *dst, device&,
unsigned *flags = NULL); unsigned *flags = NULL);
int conv_to_posix_path (PWCHAR src_path, char *posix_path,
int keep_rel_p);
int conv_to_posix_path (const char *src_path, char *posix_path, int conv_to_posix_path (const char *src_path, char *posix_path,
int keep_rel_p); int keep_rel_p);
struct mntent *getmntent (int x); struct mntent *getmntent (int x);

View File

@ -32,7 +32,10 @@ sys_wcstombs (char *tgt, int tlen, const WCHAR *src, int slen)
ret = WideCharToMultiByte (get_cp (), 0, src, slen, tgt, tlen, NULL, NULL); ret = WideCharToMultiByte (get_cp (), 0, src, slen, tgt, tlen, NULL, NULL);
if (ret) if (ret)
tgt[ret < tlen ? ret : tlen - 1] = '\0'; {
ret = (ret < tlen) ? ret : tlen - 1;
tgt[ret] = '\0';
}
return ret; return ret;
} }