* dir.cc (mkdir): Check last path component for "..".

(rmdir): Don't check last path component for "..".
	* fhandler_disk_file.cc (fhandler_disk_file::rmdir): Drop kludge
	which tries to allow deleting the current working directory.
	* path.cc (has_dot_last_component): Add parameter to indicate testing
	for "..".  Take trailing slash into account.
	(symlink_info::posixify): Rely on cygheap->cwd.win32 having a
	useful value.
	(cwdstuff::init): Initialize cygheap->cwd with current working
	directory.  Change to windows_system_directory afterwards.
	(cwdstuff::set): Never call SetCurrentDirectory here.  Just check
	if changing into target directory would be allowed.  Add comment to
	explain why.
	* path.h (has_dot_last_component): Declare with second parameter.
	* pinfo.cc (pinfo::zap_cwd): Remove.
	(pinfo::exit): Drop call to zap_cwd.
	* pinfo.h (class pinfo): Remove declaration of zap_cwd.
	* spawn.cc (spawn_guts): Set current working directory for non-Cygwin
	child applications.  Drop call to zap_cwd.
This commit is contained in:
Corinna Vinschen 2006-11-30 10:17:24 +00:00
parent 7d79436443
commit 8eca536272
8 changed files with 118 additions and 97 deletions

View File

@ -1,3 +1,25 @@
2006-11-29 Corinna Vinschen <corinna@vinschen.de>
* dir.cc (mkdir): Check last path component for "..".
(rmdir): Don't check last path component for "..".
* fhandler_disk_file.cc (fhandler_disk_file::rmdir): Drop kludge
which tries to allow deleting the current working directory.
* path.cc (has_dot_last_component): Add parameter to indicate testing
for "..". Take trailing slash into account.
(symlink_info::posixify): Rely on cygheap->cwd.win32 having a
useful value.
(cwdstuff::init): Initialize cygheap->cwd with current working
directory. Change to windows_system_directory afterwards.
(cwdstuff::set): Never call SetCurrentDirectory here. Just check
if changing into target directory would be allowed. Add comment to
explain why.
* path.h (has_dot_last_component): Declare with second parameter.
* pinfo.cc (pinfo::zap_cwd): Remove.
(pinfo::exit): Drop call to zap_cwd.
* pinfo.h (class pinfo): Remove declaration of zap_cwd.
* spawn.cc (spawn_guts): Set current working directory for non-Cygwin
child applications. Drop call to zap_cwd.
2006-11-28 Corinna Vinschen <corinna@vinschen.de> 2006-11-28 Corinna Vinschen <corinna@vinschen.de>
* security.cc (create_token): Revert erroneous change to test * security.cc (create_token): Revert erroneous change to test

View File

@ -277,7 +277,7 @@ mkdir (const char *dir, mode_t mode)
debug_printf ("got %d error from build_fh_name", fh->error ()); debug_printf ("got %d error from build_fh_name", fh->error ());
set_errno (fh->error ()); set_errno (fh->error ());
} }
else if (has_dot_last_component (dir)) else if (has_dot_last_component (dir, true))
set_errno (fh->exists () ? EEXIST : ENOENT); set_errno (fh->exists () ? EEXIST : ENOENT);
else if (!fh->mkdir (mode)) else if (!fh->mkdir (mode))
res = 0; res = 0;
@ -307,7 +307,7 @@ rmdir (const char *dir)
debug_printf ("got %d error from build_fh_name", fh->error ()); debug_printf ("got %d error from build_fh_name", fh->error ());
set_errno (fh->error ()); set_errno (fh->error ());
} }
else if (has_dot_last_component (dir)) else if (has_dot_last_component (dir, false))
set_errno (fh->exists () ? EINVAL : ENOENT); set_errno (fh->exists () ? EINVAL : ENOENT);
else if (!fh->rmdir ()) else if (!fh->rmdir ())
res = 0; res = 0;

View File

@ -1449,70 +1449,42 @@ fhandler_disk_file::rmdir ()
SetFileAttributes (get_win32_name (), SetFileAttributes (get_win32_name (),
(DWORD) pc & ~FILE_ATTRIBUTE_READONLY); (DWORD) pc & ~FILE_ATTRIBUTE_READONLY);
for (bool is_cwd = false; ; is_cwd = true) DWORD err, att = 0;
int rc = RemoveDirectory (get_win32_name ());
if (isremote () && exists ())
att = GetFileAttributes (get_win32_name ());
/* Sometimes smb indicates failure when it really succeeds, so check for
this case specifically. */
if (rc || att == INVALID_FILE_ATTRIBUTES)
{ {
DWORD err, att = 0; /* RemoveDirectory on a samba drive doesn't return an error if the
int rc = RemoveDirectory (get_win32_name ()); directory can't be removed because it's not empty. Checking for
existence afterwards keeps us informed about success. */
if (!isremote () || att == INVALID_FILE_ATTRIBUTES)
return res;
if (isremote () && exists ()) err = ERROR_DIR_NOT_EMPTY;
att = GetFileAttributes (get_win32_name ());
/* Sometimes smb indicates failure when it really succeeds, so check for
this case specifically. */
if (rc || att == INVALID_FILE_ATTRIBUTES)
{
/* RemoveDirectory on a samba drive doesn't return an error if the
directory can't be removed because it's not empty. Checking for
existence afterwards keeps us informed about success. */
if (!isremote () || att == INVALID_FILE_ATTRIBUTES)
{
res = 0;
break;
}
err = ERROR_DIR_NOT_EMPTY;
}
else
err = GetLastError ();
/* This kludge detects if we are attempting to remove the current working
directory. If so, we will move elsewhere to potentially allow the
rmdir to succeed. This means that cygwin's concept of the current
working directory != Windows concept but, hey, whaddaregonnado?
Note that this will not cause something like the following to work:
$ cd foo
$ rmdir .
since the shell will have foo "open" in the above case and so Windows
will not allow the deletion. (Actually it does on 9X.)
FIXME: A potential workaround for this is for cygwin apps to *never*
call SetCurrentDirectory. */
extern char windows_system_directory[];
if (strcasematch (get_win32_name (), cygheap->cwd.win32)
&& !strcasematch (windows_system_directory, cygheap->cwd.win32)
&& !is_cwd
&& SetCurrentDirectory (windows_system_directory))
continue;
/* On 9X ERROR_ACCESS_DENIED is returned if you try to remove a
non-empty directory. */
if (err == ERROR_ACCESS_DENIED
&& wincap.access_denied_on_delete ())
err = ERROR_DIR_NOT_EMPTY;
/* ...and, that's *not* funny, when trying to remove a non-existing
directory on a share, which is hosted by a 9x machine, the error
code ERROR_INVALID_FUNCTION is returned. */
else if (err == ERROR_INVALID_FUNCTION)
err = ERROR_FILE_NOT_FOUND;
__seterrno_from_win_error (err);
/* Directory still exists, restore its characteristics. */
if (pc.has_attribute (FILE_ATTRIBUTE_READONLY))
SetFileAttributes (get_win32_name (), (DWORD) pc);
if (is_cwd)
SetCurrentDirectory (get_win32_name ());
break;
} }
else
err = GetLastError ();
/* On 9X ERROR_ACCESS_DENIED is returned if you try to remove a
non-empty directory. */
if (err == ERROR_ACCESS_DENIED
&& wincap.access_denied_on_delete ())
err = ERROR_DIR_NOT_EMPTY;
/* ...and, that's *not* funny, when trying to remove a non-existing
directory on a share, which is hosted by a 9x machine, the error
code ERROR_INVALID_FUNCTION is returned. */
else if (err == ERROR_INVALID_FUNCTION)
err = ERROR_FILE_NOT_FOUND;
__seterrno_from_win_error (err);
/* Directory still exists, restore its characteristics. */
if (pc.has_attribute (FILE_ATTRIBUTE_READONLY))
SetFileAttributes (get_win32_name (), (DWORD) pc);
return res; return res;
} }

View File

@ -215,16 +215,28 @@ pathmatch (const char *path1, const char *path2)
Right now, normalize_posix_path will just normalize Right now, normalize_posix_path will just normalize
those components away, which changes the semantics. */ those components away, which changes the semantics. */
bool bool
has_dot_last_component (const char *dir) has_dot_last_component (const char *dir, bool test_dot_dot)
{ {
/* SUSv3: . and .. are not allowed as last components in various system /* SUSv3: . and .. are not allowed as last components in various system
calls. Don't test for backslash path separator since that's a Win32 calls. Don't test for backslash path separator since that's a Win32
path following Win32 rules. */ path following Win32 rules. */
const char *last_comp = strrchr (dir, '/'); const char *last_comp = strrchr (dir, '/');
return last_comp if (!last_comp)
&& last_comp[1] == '.' last_comp = dir;
&& (last_comp[2] == '\0' else {
|| (last_comp[2] == '.' && last_comp[3] == '\0')); /* Check for trailing slash. If so, hop back to the previous slash. */
if (!last_comp[1])
while (last_comp > dir)
if (*--last_comp == '/')
break;
if (*last_comp == '/')
++last_comp;
}
return last_comp[0] == '.'
&& ((last_comp[1] == '\0' || last_comp[1] == '/')
|| (test_dot_dot
&& last_comp[1] == '.'
&& (last_comp[2] == '\0' || last_comp[2] == '/')));
} }
#define isslash(c) ((c) == '/') #define isslash(c) ((c) == '/')
@ -3199,10 +3211,7 @@ symlink_info::posixify (char *srcbuf)
{ {
char cvtbuf[CYG_MAX_PATH + 6]; char cvtbuf[CYG_MAX_PATH + 6];
if (cygheap->cwd.win32) strncpy (cvtbuf, cygheap->cwd.win32, 2);
strncpy (cvtbuf, cygheap->cwd.win32, 2);
else
GetCurrentDirectory (CYG_MAX_PATH, cvtbuf);
strcpy (cvtbuf + 2, srcbuf); strcpy (cvtbuf + 2, srcbuf);
mount_table->conv_to_posix_path (cvtbuf, contents, 0); mount_table->conv_to_posix_path (cvtbuf, contents, 0);
} }
@ -4146,6 +4155,12 @@ void
cwdstuff::init () cwdstuff::init ()
{ {
cwd_lock.init ("cwd_lock"); cwd_lock.init ("cwd_lock");
get_initial ();
/* Actually chdir into the syste dir to avoid cwd problems. See comment
in cwdstuff::set below. */
extern char windows_system_directory[];
SetCurrentDirectory (windows_system_directory);
cwd_lock.release ();
} }
/* Get initial cwd. Should only be called once in a /* Get initial cwd. Should only be called once in a
@ -4173,17 +4188,42 @@ cwdstuff::set (const char *win32_cwd, const char *posix_cwd, bool doit)
if (win32_cwd) if (win32_cwd)
{ {
cwd_lock.acquire (); cwd_lock.acquire ();
if (doit && !SetCurrentDirectory (win32_cwd)) if (doit)
{ {
/* When calling SetCurrentDirectory for a non-existant dir on a /* Check if we *could* chdir, if we actually would.
Win9x share, it returns ERROR_INVALID_FUNCTION. */
if (GetLastError () == ERROR_INVALID_FUNCTION) Why don't we actually chdir? For two reasons:
- A process has always an open handle to the current working
directory which disallows manipulating this directory.
POSIX allows to remove a directory if the permissions are
ok. The fact that its the cwd of some process doesn't matter.
- 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. */
DWORD attr = GetFileAttributes (win32_cwd);
if (attr == INVALID_FILE_ATTRIBUTES)
{
set_errno (ENOENT); set_errno (ENOENT);
else goto out;
}
if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
{
set_errno (ENOTDIR);
goto out;
}
HANDLE h = CreateFile (win32_cwd, GENERIC_READ, wincap.shared (),
NULL, OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS, NULL);
if (h == INVALID_HANDLE_VALUE)
{
__seterrno (); __seterrno ();
goto out; goto out;
} }
CloseHandle (h);
}
} }
/* If there is no win32 path or it has the form c:xxx, get the value */ /* 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 (!win32_cwd || (isdrive (win32_cwd) && win32_cwd[2] != '\\'))

View File

@ -305,7 +305,7 @@ has_exec_chars (const char *buf, int len)
int pathmatch (const char *path1, const char *path2) __attribute__ ((regparm (2))); int pathmatch (const char *path1, const char *path2) __attribute__ ((regparm (2)));
int pathnmatch (const char *path1, const char *path2, int len) __attribute__ ((regparm (2))); int pathnmatch (const char *path1, const char *path2, int len) __attribute__ ((regparm (2)));
bool has_dot_last_component (const char *dir) __attribute__ ((regparm (1))); bool has_dot_last_component (const char *dir, bool test_dot_dot) __attribute__ ((regparm (2)));
bool fnunmunge (char *, const char *) __attribute__ ((regparm (2))); bool fnunmunge (char *, const char *) __attribute__ ((regparm (2)));

View File

@ -122,16 +122,6 @@ pinfo::maybe_set_exit_code_from_windows ()
self->pid, oexitcode, x, self->exitcode); self->pid, oexitcode, x, self->exitcode);
} }
void
pinfo::zap_cwd ()
{
extern char windows_system_directory[];
/* Move to an innocuous location to avoid a race with other processes
that may want to manipulate the current directory before this
process has completely exited. */
SetCurrentDirectory (windows_system_directory);
}
void void
pinfo::exit (DWORD n) pinfo::exit (DWORD n)
{ {
@ -148,7 +138,6 @@ pinfo::exit (DWORD n)
} }
sigproc_terminate (ES_FINAL); sigproc_terminate (ES_FINAL);
zap_cwd ();
/* FIXME: There is a potential race between an execed process and its /* FIXME: There is a potential race between an execed process and its
parent here. I hated to add a mutex just for that, though. */ parent here. I hated to add a mutex just for that, though. */

View File

@ -193,7 +193,6 @@ public:
#endif #endif
HANDLE shared_handle () {return h;} HANDLE shared_handle () {return h;}
void set_acl (); void set_acl ();
void zap_cwd ();
friend class _pinfo; friend class _pinfo;
friend class winpids; friend class winpids;
}; };

View File

@ -501,7 +501,7 @@ loop:
TRUE, /* inherit handles from parent */ TRUE, /* inherit handles from parent */
c_flags, c_flags,
envblock, /* environment */ envblock, /* environment */
0, /* use current drive/directory */ real_path.iscygexec () ? NULL : cygheap->cwd.win32,
&si, &si,
&pi); &pi);
} }
@ -536,7 +536,7 @@ loop:
TRUE, /* inherit handles from parent */ TRUE, /* inherit handles from parent */
c_flags, c_flags,
envblock, /* environment */ envblock, /* environment */
0, /* use current drive/directory */ real_path.iscygexec () ? NULL : cygheap->cwd.win32,
&si, &si,
&pi); &pi);
} }
@ -613,7 +613,6 @@ loop:
myself->sync_proc_pipe (); /* Make sure that we own wr_proc_pipe myself->sync_proc_pipe (); /* Make sure that we own wr_proc_pipe
just in case we've been previously just in case we've been previously
execed. */ execed. */
myself.zap_cwd ();
} }
orig_wr_proc_pipe = myself->dup_proc_pipe (pi.hProcess); orig_wr_proc_pipe = myself->dup_proc_pipe (pi.hProcess);
} }