/* path.cc: path support.
- Copyright 1996, 1997, 1998, 1999, 2000 Cygnus Solutions.
+ Copyright 1996, 1997, 1998, 1999, 2000 Red Hat, Inc.
This file is part of Cygwin.
Pathnames are handled as follows:
- - / is equivalent to \
+ - A \ or : in a path denotes a pure windows spec.
- Paths beginning with // (or \\) are not translated (i.e. looked
up in the mount table) and are assumed to be UNC path names.
- - Paths containing a : are not translated (paths like
- /foo/bar/baz:qux: don't make much sense but having the rule written
- this way allows one to use strchr).
The goal in the above set of rules is to allow both POSIX and Win32
flavors of pathnames without either interfering. The rules are
intended to be as close to a superset of both as possible.
- A possible future enhancement would be to allow people to
- disable/enable the mount table handling to support pure Win32
- pathnames. Hopefully this won't be needed. The suggested way to
- do this would be an environment variable because
- a) we need something that is inherited from parent to child,
- b) environment variables can be passed from the DOS shell to a
- cygwin app,
- c) it allows disabling the feature on an app by app basis within
- the same session (whereas playing about with the registry wouldn't
- -- without getting too complicated). Example:
- CYGWIN=pathrules[=@]{win32,posix}. If CYGWIN=pathrules=win32,
- mount table handling is disabled. [The intent is to have CYGWIN be
- a catchall for tweaking various cygwin.dll features].
-
Note that you can have more than one path to a file. The mount
table is always prefered when translating Win32 paths to POSIX
paths. Win32 paths in mount table entries may be UNC paths or
standard Win32 paths starting with <drive-letter>:
- In converting from a Win32 to a POSIX pathname, if there is no
- mount point that will allow the conversion to take place, a user
- mount point will be automatically created under
- cygdrive/<drive> and the translation will be redone, this
- time successfully.
-
Text vs Binary issues are not considered here in path style
- decisions.
-
- / and \ are treated as equivalent. One or the other is prefered in
- certain situations (e.g. / is preferred in result of getcwd, \ is
- preferred in arguments to Win32 api calls), but this code will
- translate as necessary.
-
- Apps wishing to translate to/from pure Win32 and POSIX-like
- pathnames can use cygwin_foo.
+ decisions, although the appropriate flags are retrieved and
+ stored in various structures.
Removing mounted filesystem support would simplify things greatly,
but having it gives us a mechanism of treating disk that lives on a
Each DOS drive is defined to have a current directory. Supporting
this would complicate things so for now things are defined so that
- c: means c:\.
+ c: means c:\. FIXME: Is this still true?
*/
+#include "winsup.h"
#include <stdio.h>
#include <stdlib.h>
#include <sys/mount.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
-#include "winsup.h"
#include <ctype.h>
-
-static int symlink_check_one (const char *path, char *buf, int buflen,
- DWORD& fileattr, unsigned *pflags,
- const suffix_info *suffixes,
- char *&found_suffix);
-static int normalize_win32_path (const char *cwd, const char *src, char *dst);
-static char *getcwd_inner (char *buf, size_t ulen, int posix_p);
+#include <winioctl.h>
+#include <sys/cygwin.h>
+#include <cygwin/version.h>
+#include "cygerrno.h"
+#include "fhandler.h"
+#include "path.h"
+#include "sync.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "registry.h"
+#include "security.h"
+#include <assert.h>
+
+static int normalize_win32_path (const char *src, char *dst);
static void slashify (const char *src, char *dst, int trailing_slash_p);
static void backslashify (const char *src, char *dst, int trailing_slash_p);
static int path_prefix_p_ (const char *path1, const char *path2, int len1);
-static int get_current_directory_name ();
-static NO_COPY const char escape_char = '^';
+struct symlink_info
+{
+ char buf[3 + MAX_PATH * 3];
+ char *known_suffix;
+ char *ext_here;
+ char *contents;
+ unsigned pflags;
+ DWORD fileattr;
+ int is_symlink;
+ int error;
+ symlink_info (): known_suffix (NULL), contents (buf + MAX_PATH + 1) {}
+ int check (const char *path, const suffix_info *suffixes);
+};
-/********************** Path Helper Functions *************************/
+cwdstuff cygcwd; /* The current working directory. */
#define path_prefix_p(p1, p2, l1) \
((tolower(*(p1))==tolower(*(p2))) && \
(((x) & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_DIRECTORY)) == \
FILE_ATTRIBUTE_SYSTEM)
+/* Determine if path prefix matches current cygdrive */
+#define iscygdrive(path) \
+ (path_prefix_p (cygwin_shared->mount.cygdrive, (path), cygwin_shared->mount.cygdrive_len))
+
+#define iscygdrive_device(path) \
+ (iscygdrive(path) && isalpha(path[cygwin_shared->mount.cygdrive_len]) && \
+ (isdirsep(path[cygwin_shared->mount.cygdrive_len + 1]) || \
+ !path[cygwin_shared->mount.cygdrive_len + 1]))
+
+#define ischrootpath(path) \
+ (myself->rootlen && \
+ strncasematch (myself->root, path, myself->rootlen) && \
+ (path[myself->rootlen] == '/' || path[myself->rootlen] == '\0'))
+
/* Return non-zero if PATH1 is a prefix of PATH2.
Both are assumed to be of the same path style and / vs \ usage.
Neither may be "".
/foo is not a prefix of /foobar
*/
-/* Determine if path prefix matches current cygdrive */
-#define iscygdrive(path) \
- (path_prefix_p (cygwin_shared->mount.cygdrive, (path), cygwin_shared->mount.cygdrive_len))
-
-#define iscygdrive_device(path) \
- (iscygdrive(path) && isalpha(path[cygwin_shared->mount.cygdrive_len]) && \
- (isdirsep(path[cygwin_shared->mount.cygdrive_len + 1]) || \
- !path[cygwin_shared->mount.cygdrive_len + 1]))
-
-/******************** Directory-related Support **************************/
-
-/* Cache getcwd value. FIXME: We need a lock for these in order to
- support multiple threads. */
-
-#ifdef _MT_SAFE
-#define current_directory_name _reent_winsup()->_current_directory_name
-#define current_directory_posix_name _reent_winsup()->_current_directory_posix_name
-#define current_directory_hash _reent_winsup()->_current_directory_hash
-#else
-static char *current_directory_name;
-static char *current_directory_posix_name;
-static unsigned long current_directory_hash;
-#endif
-
static int
path_prefix_p_ (const char *path1, const char *path2, int len1)
{
SYMLINK_CONTENTS - just return symlink contents
*/
-path_conv::path_conv (const char *src, symlink_follow follow_mode,
- int use_full_path, const suffix_info *suffixes)
+void
+path_conv::check (const char *src, unsigned opt,
+ const suffix_info *suffixes)
{
/* This array is used when expanding symlinks. It is MAX_PATH * 2
in length so that we can hold the expanded symlink plus a
trailer. */
- char work_buf[MAX_PATH * 3 + 3];
- char tmp_buf[MAX_PATH];
char path_buf[MAX_PATH];
+ char path_copy[MAX_PATH];
+ char tmp_buf[MAX_PATH];
+ symlink_info sym;
+ bool need_directory = 0;
char *rel_path, *full_path;
- if ((error = check_null_empty_path (src)))
+ if (!(opt & PC_NULLEMPTY))
+ error = 0;
+ else if ((error = check_null_empty_path (src)))
return;
- if (use_full_path)
+ if (opt & PC_FULL)
rel_path = path_buf, full_path = this->path;
else
rel_path = this->path, full_path = path_buf;
- char *sym_buf = work_buf + MAX_PATH + 1;
/* This loop handles symlink expansion. */
int loop = 0;
path_flags = 0;
for (;;)
{
MALLOC_CHECK;
+ assert (src);
+ char *p = strrchr (src, '/');
+ if (p)
+ {
+ if (p[1] == '\0' || strcmp (p, "/.") == 0)
+ need_directory = 1;
+ }
+ else if ((p = strrchr (src, '\\')) &&
+ (p[1] == '\0' || strcmp (p, "\\.") == 0))
+ need_directory = 1;
/* Must look up path in mount table, etc. */
error = cygwin_shared->mount.conv_to_win32_path (src, rel_path,
full_path,
devn, unit, &path_flags);
MALLOC_CHECK;
- if (error != 0)
+ if (error)
return;
if (devn != FH_BAD)
{
if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
strcat (full_path, "\\");
- if (follow_mode == SYMLINK_IGNORE)
+ if (opt & PC_SYM_IGNORE)
{
fileattr = GetFileAttributesA (path);
goto out;
}
/* Make a copy of the path that we can munge up */
- char path_copy[strlen (full_path) + 2];
strcpy (path_copy, full_path);
tail = path_copy + 1 + (tail - full_path); // Point to end of copy
- *sym_buf = '\0'; // Paranoid
-
/* Scan path_copy from right to left looking either for a symlink
or an actual existing file. If an existing file is found, just
return. If a symlink is found exit the for loop.
Also: be careful to preserve the errno returned from
- symlink_check_one as the caller may need it. */
+ symlink.check as the caller may need it. */
/* FIXME: Do we have to worry about multiple \'s here? */
int component = 0; // Number of translated components
- DWORD attr;
+ sym.contents[0] = '\0';
+
for (;;)
{
- save_errno s (0);
- unsigned dummy_flags, *fp;
const suffix_info *suff;
- /* Don't allow symlink_check_one to set anything in the path_conv
+ /* Don't allow symlink.check to set anything in the path_conv
class if we're working on an inner component of the path */
if (component)
{
- fp = &dummy_flags;
suff = NULL;
+ sym.pflags = 0;
}
else
{
- fp = &path_flags;
suff = suffixes;
+ sym.pflags = path_flags;
}
- MALLOC_CHECK;
- int len = symlink_check_one (path_copy, sym_buf, MAX_PATH, attr,
- fp, suff, known_suffix);
- MALLOC_CHECK;
- /* If symlink_check_one found an existing non-symlink file, then
- it returns a length of 0 and sets errno to EINVAL. It also sets
- any suffix found into `sym_buf'. */
- if (!len && get_errno () == EINVAL)
+ int len = sym.check (path_copy, suff);
+
+ if (!component)
+ path_flags = sym.pflags;
+
+ /* If symlink.check found an existing non-symlink file, then
+ it sets the appropriate flag. It also sets any suffix found
+ into `ext_here'. */
+ if (!sym.is_symlink && sym.fileattr != (DWORD) -1)
{
+ error = sym.error;
if (component == 0)
{
- fileattr = attr;
- if (follow_mode == SYMLINK_CONTENTS)
- goto out;
- else if (*sym_buf)
- {
- known_suffix = strchr (this->path, '\0');
- strcpy (known_suffix, sym_buf);
- }
- else if (known_suffix)
- known_suffix = this->path + (known_suffix - path_copy);
+ fileattr = sym.fileattr;
+ goto fillin;
}
goto out; // file found
}
these operations again on the newly derived path. */
else if (len > 0)
{
- if (component == 0)
+ if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW))
{
- if (follow_mode != SYMLINK_FOLLOW)
- {
- set_symlink (); // last component of path's a symlink.
- fileattr = attr;
- if (follow_mode == SYMLINK_CONTENTS)
- strcpy (path, sym_buf);
- goto out;
- }
+ set_symlink (); // last component of path is a symlink.
+ fileattr = sym.fileattr;
+ if (opt & PC_SYM_CONTENTS)
+ strcpy (path, sym.contents);
+ goto fillin;
}
break;
}
- s.reset (); // remember errno from symlink_check_one
+ /* No existing file found. */
if (!(tail = strrchr (path_copy, '\\')) ||
(tail > path_copy && tail[-1] == ':'))
- goto out; // all done
+ goto out; // all done
/* Haven't found a valid pathname component yet.
Pinch off the tail and try again. */
tail = full_path + (tail - path_copy);
int taillen = strlen (tail);
- int buflen = strlen (sym_buf);
+ int buflen = strlen (sym.contents);
if (buflen + taillen > MAX_PATH)
{
error = ENAMETOOLONG;
}
/* Copy tail of full_path to discovered symlink. */
- char *p;
- for (p = sym_buf + buflen; *tail; tail++)
+ for (p = sym.contents + buflen; *tail; tail++)
*p++ = *tail == '\\' ? '/' : *tail;
*p = '\0';
/* If symlink referred to an absolute path, then we
- just use sym_buf and loop. Otherwise tack the head of
- path_copy before sym_buf and translate it back from a
+ just use sym.contents and loop. Otherwise tack the head of
+ path_copy before sym.contents and translate it back from a
Win32-style path to a POSIX-style one. */
- if (isabspath (sym_buf))
- src = sym_buf;
+ if (isabspath (sym.contents))
+ src = sym.contents;
else if (!(tail = strrchr (path_copy, '\\')))
system_printf ("problem parsing %s - '%s'", src, full_path);
else
{
int headlen = 1 + tail - path_copy;
- p = sym_buf - headlen;
+ p = sym.contents - headlen;
memcpy (p, path_copy, headlen);
MALLOC_CHECK;
error = cygwin_shared->mount.conv_to_posix_path (p, tmp_buf, 1);
src = tmp_buf;
}
}
+
+fillin:
+ if (sym.known_suffix)
+ known_suffix = this->path + (sym.known_suffix - path_copy);
+ else if (sym.ext_here && !(opt & PC_SYM_CONTENTS))
+ {
+ known_suffix = strchr (this->path, '\0');
+ strcpy (known_suffix, sym.ext_here);
+ }
+
out:
+ /* Deal with Windows stupidity which considers filename\. to be valid
+ even when "filename" is not a directory. */
+ if (!need_directory || error)
+ /* nothing to do */;
+ else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
+ path_flags &= ~PATH_SYMLINK;
+ else
+ {
+ debug_printf ("%s is a non-directory", path);
+ error = ENOTDIR;
+ return;
+ }
DWORD serial, volflags;
- char root[strlen(full_path) + 10];
- strcpy (root, full_path);
- if (!rootdir (root) ||
- !GetVolumeInformation (root, NULL, 0, &serial, NULL, &volflags, NULL, 0))
+ strcpy (tmp_buf, full_path);
+ if (!rootdir (tmp_buf) ||
+ !GetVolumeInformation (tmp_buf, NULL, 0, &serial, NULL, &volflags, NULL, 0))
{
debug_printf ("GetVolumeInformation(%s) = ERR, full_path(%s), set_has_acls(FALSE)",
- root, full_path, GetLastError ());
+ tmp_buf, full_path, GetLastError ());
set_has_acls (FALSE);
}
else
{
debug_printf ("GetVolumeInformation(%s) = OK, full_path(%s), set_has_acls(%d)",
- root, full_path, volflags & FS_PERSISTENT_ACLS);
+ tmp_buf, full_path, volflags & FS_PERSISTENT_ACLS);
set_has_acls (volflags & FS_PERSISTENT_ACLS);
}
}
"\\dev\\st%d",
"nul",
"\\dev\\zero",
+ "\\dev\\%srandom",
+ "\\dev\\mem",
+ "\\dev\\clipboard",
};
static int
{
DWORD devn = FH_BAD;
- if (strncasecmp (w32path, "\\\\.\\tape", 8) == 0)
+ if (strncasematch (w32path, "\\\\.\\tape", 8))
{
devn = FH_TAPE;
unit = digits (w32path + 8);
// norewind tape devices have leading n in name
- if (! strncasecmp (uxname, "/dev/n", 6))
+ if (strncasematch (uxname, "/dev/n", 6))
unit += 128;
}
- else if (isalpha (w32path[4]) && w32path[5] == ':')
+ else if (isdrive (w32path + 4))
{
devn = FH_FLOPPY;
unit = tolower (w32path[4]) - 'a';
}
- else if (strncasecmp (w32path, "\\\\.\\physicaldrive", 17) == 0)
+ else if (strncasematch (w32path, "\\\\.\\physicaldrive", 17))
{
devn = FH_FLOPPY;
unit = digits (w32path + 17) + 128;
devn = FH_NULL;
else if (deveq ("zero"))
devn = FH_ZERO;
+ else if (deveq ("random") || deveq ("urandom"))
+ {
+ devn = FH_RANDOM;
+ unit = 8 + (deveqn ("u", 1) ? 1 : 0); /* Keep unit Linux conformant */
+ }
+ else if (deveq ("mem"))
+ {
+ devn = FH_MEM;
+ unit = 1;
+ }
+ else if (deveq ("clipboard"))
+ devn = FH_CLIPBOARD;
+ else if (deveq ("port"))
+ {
+ devn = FH_MEM;
+ unit = 4;
+ }
else if (deveqn ("com", 3) && (unit = digits (name + 3)) >= 0)
devn = FH_SERIAL;
else if (deveq ("pipe") || deveq ("piper") || deveq ("pipew"))
else if (! from_conv)
devn = get_raw_device_number (name - 5,
path_conv (name - 5,
- SYMLINK_IGNORE).get_win32 (),
+ PC_SYM_IGNORE).get_win32 (),
unit);
}
else if (deveqn ("com", 3) && (unit = digits (name + 3)) >= 0)
if ((devfmt = windows_device_names[FHDEVN (devn)]) == NULL)
return FALSE;
- __small_sprintf (win32_path, devfmt, unit);
+ if (devn == FH_RANDOM)
+ __small_sprintf (win32_path, devfmt, unit == 8 ? "" : "u");
+ else
+ __small_sprintf (win32_path, devfmt, unit);
return TRUE;
}
All duplicate /'s, except for 2 leading /'s, are deleted.
The result is 0 for success, or an errno error value. */
-static __inline int
-normalize_posix_path (const char *cwd, const char *src, char *dst)
+#define isslash(c) ((c) == '/')
+
+static int
+normalize_posix_path (const char *src, char *dst)
{
const char *src_start = src;
char *dst_start = dst;
- if (! SLASH_P (src[0]))
+ syscall_printf ("src %s", src);
+ if (isdrive (src) || strpbrk (src, "\\:"))
+ {
+ cygwin_conv_to_full_posix_path (src, dst);
+ return 0;
+ }
+ if (!isslash (src[0]))
{
- if (strlen (cwd) + 1 + strlen (src) >= MAX_PATH)
+ if (!cygcwd.get (dst))
+ return get_errno ();
+ if (strlen (dst) + 1 + strlen (src) >= MAX_PATH)
{
debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
return ENAMETOOLONG;
}
- strcpy (dst, cwd);
dst = strchr (dst, '\0');
- if (dst > dst_start && !isdirsep(dst[-1]))
+ if (*src == '.')
+ goto sawdot;
+ if (dst > dst_start && !isslash (dst[-1]))
*dst++ = '/';
}
/* Two leading /'s? If so, preserve them. */
- else if (SLASH_P (src[1]))
+ else if (isslash (src[1]))
{
+ if (myself->rootlen)
+ {
+ debug_printf ("ENOENT = normalize_posix_path (%s)", src);
+ return ENOENT;
+ }
*dst++ = '/';
*dst++ = '/';
src += 2;
- if (SLASH_P(*src))
+ if (isslash (*src))
{ /* Starts with three or more slashes - reset. */
dst = dst_start;
*dst++ = '/';
src = src_start + 1;
}
}
+ /* Exactly one leading slash. Absolute path. Check for chroot. */
+ else if (myself->rootlen)
+ {
+ strcpy (dst, myself->root);
+ dst += myself->rootlen;
+ }
while (*src)
{
/* Strip runs of /'s. */
- if (SLASH_P (*src))
- {
- *dst++ = '/';
- src++;
- while (SLASH_P(*src))
- src++;
- }
- /* Ignore "./". */
- else if (src[0] == '.' && SLASH_P (src[1])
- && (src == src_start || SLASH_P (src[-1])))
- {
- src += 2;
- while(SLASH_P(src[0]))
- src++;
- }
- /* Backup if "..". */
- else if (src[0] == '.' && src[1] == '.'
- /* dst must be greater than dst_start */
- && isdirsep (dst[-1])
- && (SLASH_P (src[2]) || src[2] == 0))
- {
- /* Back up over /, but not if it's the first one. */
- if (dst > dst_start + 1)
- dst--;
- /* Now back up to the next /. */
- while (dst > dst_start + 1 && !isdirsep (dst[-1]))
- dst--;
- src += 2;
- while (SLASH_P (*src))
- src++;
- }
- /* Otherwise, add char to result. */
+ if (!isslash (*src))
+ *dst++ = *src++;
else
{
- if (*src == '\\')
- *dst++ = '/';
- else
- *dst++ = *src;
- ++src;
+ while (*++src)
+ {
+ if (isslash (*src))
+ continue;
+
+ if (*src != '.')
+ break;
+
+ sawdot:
+ if (src[1] != '.')
+ {
+ if (!src[1])
+ {
+ if (dst == dst_start)
+ *dst++ = '/';
+ goto done;
+ }
+ if (!isslash (src[1]))
+ break;
+ }
+ else if (src[2] && !isslash (src[2]))
+ break;
+ else
+ {
+ if (!ischrootpath (dst_start) ||
+ dst - dst_start != (int) myself->rootlen)
+ while (dst > dst_start && !isslash (*--dst))
+ continue;
+ src++;
+ }
+ }
+
+ *dst++ = '/';
}
}
- *dst = 0;
+
+done:
+ *dst = '\0';
+ if (--dst > dst_start && isslash (*dst))
+ *dst = '\0';
+
debug_printf ("%s = normalize_posix_path (%s)", dst_start, src_start);
return 0;
}
The result is 0 for success, or an errno error value.
FIXME: A lot of this should be mergeable with the POSIX critter. */
-
static int
-normalize_win32_path (const char *cwd, const char *src, char *dst)
+normalize_win32_path (const char *src, char *dst)
{
const char *src_start = src;
char *dst_start = dst;
+ char *dst_root_start = dst;
- if (! SLASH_P (src[0])
- && strchr (src, ':') == NULL)
+ if (!SLASH_P (src[0]) && strchr (src, ':') == NULL)
{
- if (strlen (cwd) + 1 + strlen (src) >= MAX_PATH)
+ if (!cygcwd.get (dst, 0))
+ return get_errno ();
+ if (strlen (dst) + 1 + strlen (src) >= MAX_PATH)
{
debug_printf ("ENAMETOOLONG = normalize_win32_path (%s)", src);
return ENAMETOOLONG;
}
- strcpy (dst, cwd);
dst += strlen (dst);
- *dst++ = '\\';
+ if (!SLASH_P (dst[-1]))
+ *dst++ = '\\';
}
/* Two leading \'s? If so, preserve them. */
else if (SLASH_P (src[0]) && SLASH_P (src[1]))
{
+ if (myself->rootlen)
+ {
+ debug_printf ("ENOENT = normalize_win32_path (%s)", src);
+ return ENOENT;
+ }
*dst++ = '\\';
++src;
}
+ /* If absolute path, care for chroot. */
+ else if (SLASH_P (src[0]) && !SLASH_P (src[1]) && myself->rootlen)
+ {
+ strcpy (dst, myself->root);
+ char *c;
+ while ((c = strchr (dst, '/')) != NULL)
+ *c = '\\';
+ dst += myself->rootlen;
+ dst_root_start = dst;
+ *dst++ = '\\';
+ }
while (*src)
{
&& (SLASH_P (src[2]) || src[2] == 0))
{
/* Back up over /, but not if it's the first one. */
- if (dst > dst_start + 1)
+ if (dst > dst_root_start + 1)
dst--;
/* Now back up to the next /. */
- while (dst > dst_start + 1 && dst[-1] != '\\' && dst[-2] != ':')
+ while (dst > dst_root_start + 1 && dst[-1] != '\\' && dst[-2] != ':')
dst--;
src += 2;
if (SLASH_P (*src))
void
mount_info::init ()
{
- int found_slash = 0;
-
nmounts = 0;
had_to_create_mount_areas = 0;
/* Fetch the mount table and cygdrive-related information from
the registry. */
from_registry ();
-
- /* If slash isn't already mounted, mount system directory as slash. */
- if (nmounts != 0)
- for (int i = 0; i < nmounts; i++)
- {
- if (strcmp (mount[i].posix_path, "/") == 0)
- {
- found_slash = 1;
- break;
- }
- }
-
- if (!found_slash)
- mount_slash ();
-}
-
-/* mount_slash: mount the system partition as slash. */
-
-void
-mount_info::mount_slash ()
-{
- char drivestring[MAX_PATH];
- GetSystemDirectory (drivestring, MAX_PATH);
- drivestring[2] = 0; /* truncate path to "<drive>:" */
-
- if (add_reg_mount (drivestring, "/", 0) == 0)
- add_item (drivestring, "/", 0);
}
/* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
int
mount_info::conv_to_win32_path (const char *src_path, char *win32_path,
- char *full_win32_path, DWORD &devn, int &unit, unsigned *flags)
+ char *full_win32_path, DWORD &devn, int &unit,
+ unsigned *flags)
{
int src_path_len = strlen (src_path);
- int trailing_slash_p = (src_path_len > 0
+ int trailing_slash_p = (src_path_len > 1
&& SLASH_P (src_path[src_path_len - 1]));
MALLOC_CHECK;
int isrelpath;
mount_item *mi = NULL; /* initialized to avoid compiler warning */
char pathbuf[MAX_PATH];
- /* The rule is :'s can't appear in [our] POSIX path names so this is a safe
- test; if ':' is present it already be in Win32 form. */
- if (strchr (src_path, ':') != NULL)
+ /* Determine where the destination should be placed. */
+ if (full_win32_path != NULL)
+ dst = full_win32_path;
+ else if (win32_path != NULL)
+ dst = win32_path;
+
+ if (dst == NULL)
+ goto out; /* Sanity check. */
+
+ /* An MS-DOS spec has either a : or a \. If this is found, short
+ circuit most of the rest of this function. */
+ if (strpbrk (src_path, ":\\") != NULL)
{
debug_printf ("%s already win32", src_path);
- rc = normalize_win32_path ("", src_path, pathbuf);
+ rc = normalize_win32_path (src_path, dst);
if (rc)
- return rc;
- /* FIXME: Do we have to worry about trailing_slash_p here? */
- if (win32_path != NULL)
- strcpy (win32_path, pathbuf);
- if (full_win32_path != NULL)
- strcpy (full_win32_path, pathbuf);
- *flags = set_flags_from_win32_path (pathbuf);
- goto out;
+ {
+ debug_printf ("normalize_win32_path failed, rc %d", rc);
+ return rc;
+ }
+ isrelpath = !isabspath (src_path);
+ *flags = set_flags_from_win32_path (dst);
+ if (myself->rootlen && dst[0] && dst[1] == ':')
+ {
+ char posix_path[MAX_PATH + 1];
+
+ rc = cygwin_shared->mount.conv_to_posix_path (dst, posix_path, 0);
+ if (rc)
+ {
+ debug_printf ("conv_to_posix_path failed, rc %d", rc);
+ return rc;
+ }
+ if (!ischrootpath (posix_path))
+ {
+ debug_printf ("ischrootpath failed");
+ return ENOENT;
+ }
+ }
+ goto fillin;
}
/* Normalize the path, taking out ../../ stuff, we need to do this
converting it to a DOS-style path, looking up the appropriate drive
in the mount table. */
- char cwd[MAX_PATH];
-
- /* No need to fetch cwd if path is absolute. */
- if ((isrelpath = ! SLASH_P (*src_path)))
- getcwd_inner (cwd, MAX_PATH, TRUE); /* FIXME: check rc */
- else
- strcpy (cwd, "/"); /* some innocuous value */
+ rc = normalize_posix_path (src_path, pathbuf);
- rc = normalize_posix_path (cwd, src_path, pathbuf);
- MALLOC_CHECK;
- if (rc != 0)
+ if (rc)
{
debug_printf ("%d = conv_to_win32_path (%s)", rc, src_path);
*flags = 0;
return rc;
}
- nofinalslash (pathbuf, pathbuf);
-
- /* Determine where the destination should be placed. */
- if (full_win32_path != NULL)
- dst = full_win32_path;
- else if (win32_path != NULL)
- dst = win32_path;
- if (dst == NULL)
- goto out; /* Sanity check. */
+ isrelpath = !isslash (*src_path);
/* See if this is a cygwin "device" */
if (win32_device_name (pathbuf, dst, devn, unit))
else
{
int n = mi->native_pathlen;
- memcpy (dst, mi->native_path, n);
+ memcpy (dst, mi->native_path, n + 1);
char *p = pathbuf + mi->posix_pathlen;
if (!trailing_slash_p && !*p)
- dst[n] = '\0';
+ {
+ if (isdrive (dst) && !dst[2])
+ dst[n++] = '\\';
+ dst[n] = '\0';
+ }
else
{
/* Do not add trailing \ to UNC device names like \\.\a: */
fillin:
/* Compute relative path if asked to and able to. */
- unsigned cwdlen;
- cwdlen = 0; /* avoid a (hopefully) bogus compiler warning */
if (win32_path == NULL)
/* nothing to do */;
- else if (isrelpath &&
- path_prefix_p (current_directory_name, dst,
- cwdlen = strlen (current_directory_name)))
- {
- if (strlen (dst) == cwdlen)
- dst += cwdlen;
+ else if (isrelpath)
+ {
+ char cwd_win32[MAX_PATH];
+ if (!cygcwd.get (cwd_win32, 0))
+ return get_errno ();
+ unsigned cwdlen = strlen (cwd_win32);
+ if (!path_prefix_p (cwd_win32, dst, cwdlen))
+ strcpy (win32_path, dst);
else
- dst += isdirsep (current_directory_name[cwdlen - 1]) ? cwdlen : cwdlen + 1;
-
- memmove (win32_path, dst, strlen (dst) + 1);
- if (!*win32_path)
{
- strcpy (win32_path, ".");
- if (trailing_slash_p)
- strcat (win32_path, "\\");
+ size_t n = strlen (dst);
+ if (n < cwdlen)
+ strcpy (win32_path, dst);
+ else
+ {
+ if (n == cwdlen)
+ dst += cwdlen;
+ else
+ dst += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
+
+ memmove (win32_path, dst, strlen (dst) + 1);
+ if (!*win32_path)
+ {
+ strcpy (win32_path, ".");
+ if (trailing_slash_p)
+ strcat (win32_path, "\\");
+ }
+ }
}
}
else if (win32_path != dst)
The cygdrive prefix always ends with a trailing slash so
the drive letter is added after the path. */
dst[len++] = tolower (src[0]);
- if (!src[2])
+ if (!src[2] || (SLASH_P (src[2]) && !src[3]))
dst[len++] = '\000';
else
{
+ int n;
dst[len++] = '/';
- strcpy (dst + len, src + 3);
+ if (SLASH_P (src[2]))
+ n = 3;
+ else
+ n = 2;
+ strcpy (dst + len, src + n);
}
slashify (dst, dst, trailing_slash_p);
}
int keep_rel_p)
{
int src_path_len = strlen (src_path);
- int trailing_slash_p = (src_path_len > 0
- && SLASH_P (src_path[src_path_len - 1]));
- int relative_path_p = (! SLASH_P (*src_path)
- && strchr (src_path, ':') == NULL);
+ int relative_path_p = !isabspath (src_path);
+ int trailing_slash_p;
+
+ if (src_path_len <= 1)
+ trailing_slash_p = 0;
+ else
+ {
+ const char *lastchar = src_path + src_path_len - 1;
+ trailing_slash_p = SLASH_P (*lastchar) && lastchar[-1] != ':';
+ }
- debug_printf ("conv_to_posix_path (%s, %s)", src_path,
- keep_rel_p ? "keep-rel" : "no-keep-rel");
+ debug_printf ("conv_to_posix_path (%s, %s, %s)", src_path,
+ keep_rel_p ? "keep-rel" : "no-keep-rel",
+ trailing_slash_p ? "add-slash" : "no-add-slash");
MALLOC_CHECK;
if (src_path_len >= MAX_PATH)
/* FIXME: For now, if the path is relative and it's supposed to stay
that way, skip mount table processing. */
- MALLOC_CHECK;
+
if (keep_rel_p && relative_path_p)
{
slashify (src_path, posix_path, 0);
}
char pathbuf[MAX_PATH];
- char cwd[MAX_PATH];
-
- /* No need to fetch cwd if path is absolute. */
- if (relative_path_p)
- getcwd_inner (cwd, MAX_PATH, 0); /* FIXME: check rc */
- else
- strcpy (cwd, "/"); /* some innocuous value */
- MALLOC_CHECK;
- int rc = normalize_win32_path (cwd, src_path, pathbuf);
- MALLOC_CHECK;
+ int rc = normalize_win32_path (src_path, pathbuf);
if (rc != 0)
{
debug_printf ("%d = conv_to_posix_path (%s)", rc, src_path);
return rc;
}
- nofinalslash (pathbuf, pathbuf);
- MALLOC_CHECK;
int pathbuflen = strlen (pathbuf);
for (int i = 0; i < nmounts; ++i)
/* SRC_PATH is in the mount table. */
int nextchar;
- if (!pathbuf[mi.native_pathlen])
+ const char *p = pathbuf + mi.native_pathlen;
+
+ if (!*p || !p[1])
nextchar = 0;
- else if (isdirsep (pathbuf[mi.native_pathlen]))
+ else if (isdirsep (*p))
nextchar = -1;
else
nextchar = 1;
if (addslash)
strcat (posix_path, "/");
if (nextchar)
- slashify (pathbuf + mi.native_pathlen,
+ slashify (p,
posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
- trailing_slash_p);
+ trailing_slash_p);
goto out;
}
letter not covered by the mount table. If it's a relative path then the
caller must want an absolute path (otherwise we would have returned
above). So we always return an absolute path at this point. */
- if ((isalpha (pathbuf[0])) && (pathbuf[1] == ':'))
- cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p &&
- pathbuflen > 3);
+ if (isdrive (pathbuf))
+ cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p);
else
{
/* The use of src_path and not pathbuf here is intentional.
char posix_path[MAX_PATH];
HKEY key = r.get_key ();
DWORD i, posix_path_size;
-
-loop:
- for (i = 0; ;i++)
- {
- posix_path_size = MAX_PATH;
- LONG err = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
- NULL, NULL, NULL);
-
- if (err != ERROR_SUCCESS)
- break;
-
- if (iscygdrive (posix_path))
- {
- /* This shouldn't be in the mount table. */
- (void) r.kill (posix_path);
- goto loop;
- }
- }
+ int res;
/* Loop through subkeys */
/* FIXME: we would like to not check MAX_MOUNTS but the heap in the
shared area is currently statically allocated so we can't have an
arbitrarily large number of mounts. */
- for (DWORD i = 0; i < MAX_MOUNTS; i++)
+ for (i = 0; ; i++)
{
char native_path[MAX_PATH];
int mount_flags;
/* FIXME: if maximum posix_path_size is 256, we're going to
run into problems if we ever try to store a mount point that's
over 256 but is under MAX_PATH! */
- LONG err = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
+ res = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
NULL, NULL, NULL);
- if (err == ERROR_NO_MORE_ITEMS)
+ if (res == ERROR_NO_MORE_ITEMS)
break;
- else if (err != ERROR_SUCCESS)
+ else if (res != ERROR_SUCCESS)
{
- debug_printf ("RegEnumKeyEx failed, error %d!\n", err);
+ debug_printf ("RegEnumKeyEx failed, error %d!\n", res);
break;
}
- if (iscygdrive (posix_path))
- {
- /* This shouldn't be in the mount table. */
- // (void) r.kill (posix_path);
- continue;
- }
-
/* Get a reg_key based on i. */
reg_key subkey = reg_key (key, KEY_READ, posix_path, NULL);
- /* Check the mount table for prefix matches. */
- for (int j = 0; j < nmounts; j++)
- if (strcasematch (mount[j].posix_path, posix_path))
- goto next; /* Can't have more than one */
-
/* Fetch info from the subkey. */
subkey.get_string ("native", native_path, sizeof (native_path), "");
mount_flags = subkey.get_int ("flags", 0);
/* Add mount_item corresponding to registry mount point. */
- cygwin_shared->mount.add_item (native_path, posix_path, mount_flags);
- next:
- continue;
+ res = cygwin_shared->mount.add_item (native_path, posix_path, mount_flags, FALSE);
+ if (res && get_errno () == EMFILE)
+ break; /* The number of entries exceeds MAX_MOUNTS */
}
}
old mounts. */
if (had_to_create_mount_areas == 2)
import_v1_mounts ();
-
- sort ();
}
/* add_reg_mount: Add mount item to registry. Return zero on success,
int
mount_info::add_reg_mount (const char * native_path, const char * posix_path, unsigned mountflags)
{
+ int res = 0;
+
/* Add the mount to the right registry location, depending on
whether MOUNT_SYSTEM is set in the mount flags. */
if (!(mountflags & MOUNT_SYSTEM)) /* current_user mount */
reg_key reg_user;
/* Start by deleting existing mount if one exists. */
- reg_user.kill (posix_path);
+ res = reg_user.kill (posix_path);
+ if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND)
+ goto err;
/* Create the new mount. */
reg_key subkey = reg_key (reg_user.get_key (),
KEY_ALL_ACCESS,
posix_path, NULL);
- subkey.set_string ("native", native_path);
- subkey.set_int ("flags", mountflags);
+ res = subkey.set_string ("native", native_path);
+ if (res != ERROR_SUCCESS)
+ goto err;
+ res = subkey.set_int ("flags", mountflags);
}
else /* local_machine mount */
{
CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
NULL);
- if (reg_sys.get_key () == INVALID_HANDLE_VALUE)
- {
- set_errno (EACCES);
- return -1;
- }
-
/* Start by deleting existing mount if one exists. */
- reg_sys.kill (posix_path);
+ res = reg_sys.kill (posix_path);
+ if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND)
+ goto err;
/* Create the new mount. */
reg_key subkey = reg_key (reg_sys.get_key (),
KEY_ALL_ACCESS,
posix_path, NULL);
- subkey.set_string ("native", native_path);
- subkey.set_int ("flags", mountflags);
+ res = subkey.set_string ("native", native_path);
+ if (res != ERROR_SUCCESS)
+ goto err;
+ res = subkey.set_int ("flags", mountflags);
}
return 0; /* Success! */
+ err:
+ __seterrno_from_win_error (res);
+ return -1;
}
/* del_reg_mount: delete mount item from registry indicated in flags.
int
mount_info::del_reg_mount (const char * posix_path, unsigned flags)
{
- int killres;
+ int res;
- if ((flags & MOUNT_SYSTEM) == 0) /* Delete from user registry */
+ if ((flags & MOUNT_SYSTEM) == 0) /* Delete from user registry */
{
reg_key reg_user (KEY_ALL_ACCESS,
CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
- killres = reg_user.kill (posix_path);
+ res = reg_user.kill (posix_path);
}
- else /* Delete from system registry */
+ else /* Delete from system registry */
{
reg_key reg_sys (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
NULL);
-
- if (reg_sys.get_key () == INVALID_HANDLE_VALUE)
- {
- set_errno (EACCES);
- return -1;
- }
-
- killres = reg_sys.kill (posix_path);
+ res = reg_sys.kill (posix_path);
}
- if (killres != ERROR_SUCCESS)
+ if (res != ERROR_SUCCESS)
{
- __seterrno_from_win_error (killres);
+ __seterrno_from_win_error (res);
return -1;
}
void
mount_info::read_cygdrive_info_from_registry ()
{
- /* reg_key for user mounts in HKEY_CURRENT_USER. */
+ /* reg_key for user path prefix in HKEY_CURRENT_USER. */
reg_key r;
if (r.get_string ("cygdrive prefix", cygdrive, sizeof (cygdrive), "") != 0)
{
- /* Didn't find it so write the default to the registry and use it. */
- write_cygdrive_info_to_registry ("/cygdrive", MOUNT_AUTO);
+ /* Didn't find the user path prefix so check the system path prefix. */
+
+ /* reg_key for system path prefix in HKEY_LOCAL_MACHINE. */
+ reg_key r2 (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+
+ if (r2.get_string ("cygdrive prefix", cygdrive, sizeof (cygdrive), "") != 0)
+ {
+ /* Didn't find either so write the default to the registry and use it.
+ NOTE: We are writing and using the user path prefix. */
+ write_cygdrive_info_to_registry ("/cygdrive", MOUNT_AUTO);
+ }
+ else
+ {
+ /* Fetch system cygdrive_flags from registry; returns MOUNT_AUTO on
+ error. */
+ cygdrive_flags = r2.get_int ("cygdrive flags", MOUNT_AUTO);
+ slashify (cygdrive, cygdrive, 1);
+ cygdrive_len = strlen(cygdrive);
+ }
}
else
{
- /* Fetch cygdrive_flags from registry; returns MOUNT_AUTO on error. */
+ /* Fetch user cygdrive_flags from registry; returns MOUNT_AUTO on
+ error. */
cygdrive_flags = r.get_int ("cygdrive flags", MOUNT_AUTO);
slashify (cygdrive, cygdrive, 1);
cygdrive_len = strlen(cygdrive);
int
mount_info::write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags)
{
- /* reg_key for user mounts in HKEY_CURRENT_USER. */
- reg_key r;
+ /* Determine whether to modify user or system cygdrive path prefix. */
+ HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+
+ /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in
+ HKEY_LOCAL_MACHINE. */
+ reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
/* Verify cygdrive prefix starts with a forward slash and if there's
another character, it's not a slash. */
if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) ||
- (cygdrive_prefix[0] != '/') ||
- ((cygdrive_prefix[1] != '\0') && (SLASH_P (cygdrive_prefix[1]))))
+ (!isslash (cygdrive_prefix[0])) ||
+ ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1]))))
{
set_errno (EINVAL);
return -1;
/* Ensure that there is never a final slash */
nofinalslash (cygdrive_prefix, hold_cygdrive_prefix);
- r.set_string ("cygdrive prefix", hold_cygdrive_prefix);
+ int res;
+ res = r.set_string ("cygdrive prefix", hold_cygdrive_prefix);
+ if (res != ERROR_SUCCESS)
+ {
+ __seterrno_from_win_error (res);
+ return -1;
+ }
r.set_int ("cygdrive flags", flags);
- /* This also needs to go in the in-memory copy of "cygdrive" */
- slashify (cygdrive_prefix, cygwin_shared->mount.cygdrive, 1);
- cygwin_shared->mount.cygdrive_flags = flags;
- cygwin_shared->mount.cygdrive_len = strlen(cygwin_shared->mount.cygdrive);
+ /* This also needs to go in the in-memory copy of "cygdrive", but only if
+ appropriate:
+ 1. setting user path prefix, or
+ 2. overwriting (a previous) system path prefix */
+ if ((flags & MOUNT_SYSTEM) == 0 ||
+ (cygwin_shared->mount.cygdrive_flags & MOUNT_SYSTEM) != 0)
+ {
+ slashify (cygdrive_prefix, cygwin_shared->mount.cygdrive, 1);
+ cygwin_shared->mount.cygdrive_flags = flags;
+ cygwin_shared->mount.cygdrive_len = strlen(cygwin_shared->mount.cygdrive);
+ }
return 0;
}
+int
+mount_info::remove_cygdrive_info_from_registry (const char *cygdrive_prefix, unsigned flags)
+{
+ /* Determine whether to modify user or system cygdrive path prefix. */
+ HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+
+ /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in
+ HKEY_LOCAL_MACHINE. */
+ reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+
+ /* Delete cygdrive prefix and flags. */
+ int res = r.killvalue ("cygdrive prefix");
+ int res2 = r.killvalue ("cygdrive flags");
+
+ /* Reinitialize the cygdrive path prefix to reflect to removal from the
+ registry. */
+ read_cygdrive_info_from_registry ();
+
+ return (res != ERROR_SUCCESS) ? res : res2;
+}
+
+int
+mount_info::get_cygdrive_prefixes (char *user, char *system)
+{
+ /* Get the user path prefix from HKEY_CURRENT_USER. */
+ reg_key r;
+ int res = r.get_string ("cygdrive prefix", user, MAX_PATH, "");
+
+ /* Get the system path prefix from HKEY_LOCAL_MACHINE. */
+ reg_key r2 (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_REGISTRY_NAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+ int res2 = r2.get_string ("cygdrive prefix", system, MAX_PATH, "");
+
+ return (res != ERROR_SUCCESS) ? res : res2;
+}
+
struct mntent *
mount_info::getmntent (int x)
{
/* The two paths were the same length, so just determine normal
lexical sorted order. */
- res = strcasecmp (ap->posix_path, bp->posix_path);
+ res = strcmp (ap->native_path, bp->native_path);
if (res == 0)
{
qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name);
}
-/* Add an entry to the in-memory mount table.
+/* Add an entry to the mount table.
Returns 0 on success, -1 on failure and errno is set.
This is where all argument validation is done. It may not make sense to
do this when called internally, but it's cleaner to keep it all here. */
int
-mount_info::add_item (const char *native, const char *posix, unsigned mountflags)
+mount_info::add_item (const char *native, const char *posix, unsigned mountflags, int reg_p)
{
- /* Can't add more than MAX_MOUNTS. */
- if (nmounts == MAX_MOUNTS)
- {
- set_errno (EMFILE);
- return -1;
- }
-
/* Something's wrong if either path is NULL or empty, or if it's
not a UNC or absolute path. */
if ((native == NULL) || (*native == 0) ||
(posix == NULL) || (*posix == 0) ||
- (!slash_unc_prefix_p (native) && !isabspath (native)))
+ !isabspath (native) || !isabspath (posix) ||
+ slash_unc_prefix_p (posix) || isdrive (posix))
{
set_errno (EINVAL);
return -1;
if (slash_drive_prefix_p (native))
slash_drive_to_win32_path (native, nativetmp, 0);
else
- {
- backslashify (native, nativetmp, 0);
- nofinalslash (nativetmp, nativetmp);
- }
+ backslashify (native, nativetmp, 0);
+ nofinalslash (nativetmp, nativetmp);
slashify (posix, posixtmp, 0);
nofinalslash (posixtmp, posixtmp);
/* Write over an existing mount item with the same POSIX path if
it exists and is from the same registry area. */
- for (int i = 0; i < nmounts; i++)
+ int i;
+ for (i = 0; i < nmounts; i++)
{
- if ((strcmp (mount[i].posix_path, posixtmp) == 0) &&
- ((mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM)))
- {
- /* replace existing mount item */
- mount[i].init (nativetmp, posixtmp, mountflags);
- goto sortit;
- }
+ if (strcasematch (mount[i].posix_path, posixtmp) &&
+ (mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM))
+ break;
+ }
+
+ if (i == nmounts && nmounts == MAX_MOUNTS)
+ {
+ set_errno (EMFILE);
+ return -1;
}
- mount[nmounts++].init (nativetmp, posixtmp, mountflags);
+ if (reg_p && add_reg_mount (nativetmp, posixtmp, mountflags))
+ return -1;
-sortit:
+ if (i == nmounts)
+ nmounts++;
+ mount[i].init (nativetmp, posixtmp, mountflags);
sort ();
return 0;
*/
int
-mount_info::del_item (const char *path, unsigned flags)
+mount_info::del_item (const char *path, unsigned flags, int reg_p)
{
char pathtmp[MAX_PATH];
+ int posix_path_p = FALSE;
/* Something's wrong if path is NULL or empty. */
- if ((path == NULL) || (*path == 0))
+ if (path == NULL || *path == 0 || !isabspath (path))
{
set_errno (EINVAL);
return -1;
}
- slashify (path, pathtmp, 0);
+ if (slash_drive_prefix_p (path))
+ slash_drive_to_win32_path (path, pathtmp, 0);
+ else if (slash_unc_prefix_p (path) || strpbrk (path, ":\\"))
+ backslashify (path, pathtmp, 0);
+ else
+ {
+ slashify (path, pathtmp, 0);
+ posix_path_p = TRUE;
+ }
nofinalslash (pathtmp, pathtmp);
- debug_printf ("%s[%s]", path, pathtmp);
+ if (reg_p && posix_path_p &&
+ del_reg_mount (pathtmp, flags) &&
+ del_reg_mount (path, flags)) /* for old irregular entries */
+ return -1;
for (int i = 0; i < nmounts; i++)
{
- /* Delete if paths and mount locations match. */
- if (((strcmp (mount[i].posix_path, pathtmp) == 0
- || strcmp (mount[i].native_path, pathtmp) == 0)) &&
- ((mount[i].flags & MOUNT_SYSTEM) == (flags & MOUNT_SYSTEM)))
+ int ent = native_sorted[i]; /* in the same order as getmntent() */
+ if (((posix_path_p)
+ ? strcasematch (mount[ent].posix_path, pathtmp)
+ : strcasematch (mount[ent].native_path, pathtmp)) &&
+ (mount[ent].flags & MOUNT_SYSTEM) == (flags & MOUNT_SYSTEM))
{
- nmounts--; /* One less mount table entry */
+ if (!posix_path_p &&
+ reg_p && del_reg_mount (mount[ent].posix_path, flags))
+ return -1;
+
+ nmounts--; /* One less mount table entry */
/* Fill in the hole if not at the end of the table */
- if (i < nmounts)
- memcpy (mount + i, mount + i + 1,
- sizeof (mount[i]) * (nmounts - i));
- sort (); /* Resort the table */
+ if (ent < nmounts)
+ memmove (mount + ent, mount + ent + 1,
+ sizeof (mount[ent]) * (nmounts - ent));
+ sort (); /* Resort the table */
return 0;
}
}
we're reading. */
mountflags |= which;
- cygwin_shared->mount.add_item (win32path, unixpath, mountflags);
+ int res = cygwin_shared->mount.add_item (win32path, unixpath, mountflags, TRUE);
+ if (res && get_errno () == EMFILE)
+ break; /* The number of entries exceeds MAX_MOUNTS */
}
}
}
-/* from_v1_registry: Build the entire mount table from the old v1 registry
- mount area. */
+/* import_v1_mounts: If v1 mounts are present, load them and write
+ the new entries to the new registry area. */
void
-mount_info::from_v1_registry ()
+mount_info::import_v1_mounts ()
{
reg_key r (HKEY_CURRENT_USER, KEY_ALL_ACCESS,
"SOFTWARE",
"mounts",
NULL);
read_v1_mounts (r1, MOUNT_SYSTEM);
-
- /* Note: we don't need to sort internal table here since it is
- done in main from_registry call after this function would be
- run. */
-}
-
-/* import_v1_mounts: If v1 mounts are present, load them and write
- the new entries to the new registry area. */
-
-void
-mount_info::import_v1_mounts ()
-{
- /* Read in old mounts into memory. */
- from_v1_registry ();
-
- /* Write all mounts to the new registry. */
- to_registry ();
-}
-
-/* to_registry: For every mount point in memory, add a corresponding
- registry mount point. */
-
-void
-mount_info::to_registry ()
-{
- for (int i = 0; i < MAX_MOUNTS; i++)
- {
- if (i < nmounts)
- {
- mount_item *p = mount + i;
-
- add_reg_mount (p->native_path, p->posix_path, p->flags);
-
- debug_printf ("%02x: %s, %s, %d",
- i, p->native_path, p->posix_path, p->flags);
- }
- }
}
/************************* mount_item class ****************************/
strcpy (cygwin_shared->mount.mnt_dir, posix_path);
ret.mnt_dir = cygwin_shared->mount.mnt_dir;
- if (!(flags & MOUNT_SYSTEM)) /* user mount */
+ if (!(flags & MOUNT_SYSTEM)) /* user mount */
strcpy (cygwin_shared->mount.mnt_type, (char *) "user");
- else /* system mount */
+ else /* system mount */
strcpy (cygwin_shared->mount.mnt_type, (char *) "system");
- if ((flags & MOUNT_AUTO)) /* cygdrive */
+ if ((flags & MOUNT_AUTO)) /* cygdrive */
strcat (cygwin_shared->mount.mnt_type, (char *) ",auto");
ret.mnt_type = cygwin_shared->mount.mnt_type;
else
strcpy (cygwin_shared->mount.mnt_opts, (char *) "binmode");
- if (flags & MOUNT_EXEC)
+ if (flags & MOUNT_CYGWIN_EXEC)
+ strcat (cygwin_shared->mount.mnt_opts, (char *) ",cygexec");
+ else if (flags & MOUNT_EXEC)
strcat (cygwin_shared->mount.mnt_opts, (char *) ",exec");
+
ret.mnt_opts = cygwin_shared->mount.mnt_opts;
ret.mnt_freq = 1;
win32_path = NULL;
}
else
- {
- if (iscygdrive (posix_path))
- {
- set_errno (EINVAL);
- return res; /* Don't try to add cygdrive prefix. */
- }
-
- res = cygwin_shared->mount.add_reg_mount (win32_path, posix_path, flags);
-
- if (res == 0)
- cygwin_shared->mount.add_item (win32_path, posix_path, flags);
- }
+ res = cygwin_shared->mount.add_item (win32_path, posix_path, flags, TRUE);
syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags);
return res;
int
cygwin_umount (const char *path, unsigned flags)
{
- int res = cygwin_shared->mount.del_reg_mount (path, flags);
+ int res = -1;
- if (res == 0)
- cygwin_shared->mount.del_item (path, flags);
+ if (flags & MOUNT_AUTO)
+ {
+ /* When flags include MOUNT_AUTO, take this to mean that we actually want
+ to remove the cygdrive prefix and flags without actually unmounting
+ anything. */
+ res = cygwin_shared->mount.remove_cygdrive_info_from_registry (path, flags);
+ }
+ else
+ {
+ res = cygwin_shared->mount.del_item (path, flags, TRUE);
+ }
syscall_printf ("%d = cygwin_umount (%s, %d)", res, path, flags);
return res;
HANDLE h;
int res = -1;
- path_conv win32_path (frompath, SYMLINK_NOFOLLOW);
+ path_conv win32_path (frompath, PC_SYM_NOFOLLOW);
if (win32_path.error)
{
set_errno (win32_path.error);
goto done;
}
- h = CreateFileA(win32_path.get_win32 (), GENERIC_WRITE, 0, &sec_none_nih,
+ h = CreateFileA(win32_path, GENERIC_WRITE, 0, &sec_none_nih,
CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
if (h == INVALID_HANDLE_VALUE)
__seterrno ();
else
{
CloseHandle (h);
- set_file_attribute (win32_path.has_acls (),
- win32_path.get_win32 (),
- S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO);
- SetFileAttributesA (win32_path.get_win32 (), FILE_ATTRIBUTE_SYSTEM);
+ set_file_attribute (win32_path.has_acls (),
+ win32_path.get_win32 (),
+ S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO);
+ SetFileAttributesA (win32_path.get_win32 (), FILE_ATTRIBUTE_SYSTEM);
res = 0;
}
}
static __inline char *
has_suffix (const char *path, const suffix_info *suffixes)
{
+ assert (path);
char *ext = strrchr (path, '.');
if (ext)
for (const suffix_info *ex = suffixes; ex->name != NULL; ex++)
Return -1 on error, 0 if PATH is not a symlink, or the length
stored into BUF if PATH is a symlink. */
-static int
-symlink_check_one (const char *in_path, char *buf, int buflen, DWORD& fileattr,
- unsigned *pflags, const suffix_info *suffixes, char *&known_suffix)
+int
+symlink_info::check (const char *in_path, const suffix_info *suffixes)
{
HANDLE h;
int res = 0;
- char extbuf[buflen + 5];
- char *ext_here;
+ char extbuf[MAX_PATH + 5];
const char *path = in_path;
if (!suffixes)
ext_here = strchr (path, '\0');
}
- *buf = '\0';
+ is_symlink = TRUE;
+
+ error = 0;
do
{
if (!next_suffix (ext_here, suffixes))
break;
+ error = 0;
fileattr = GetFileAttributesA (path);
if (fileattr == (DWORD) -1)
{
matter, so we just return 0. For example, getting the
attributes of \\HOST will typically fail. */
debug_printf ("GetFileAttributesA (%s) failed", path);
- __seterrno ();
+ error = geterrno_from_win_error (GetLastError (), EACCES);
continue;
}
- /* Windows allows path\. even when `path' isn't a directory.
- Detect this scenario and disallow it, since it is non-UNIX like. */
- char *p = strchr (path, '\0');
- if (p > path + 1 && p[-1] == '.' && SLASH_P (p[-2]) &&
- !(fileattr & FILE_ATTRIBUTE_DIRECTORY))
- {
- debug_printf ("\\. specified on non-directory");
- set_errno (ENOTDIR);
- return 0;
- }
-
/* A symlink will have the `system' file attribute. */
/* Only files can be symlinks (which can be symlinks to directories). */
- if (!(*pflags & PATH_SYMLINK) && !SYMLINKATTR (fileattr))
+ if (!(pflags & PATH_SYMLINK) && !SYMLINKATTR (fileattr))
goto file_not_symlink;
- /* Check the file's extended attributes, if it has any. */
- int unixattr = 0;
- if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
- unixattr |= S_IFDIR;
-
- if (!get_file_attribute (TRUE, path, &unixattr))
- {
- if (unixattr & STD_XBITS)
- *pflags |= PATH_EXEC;
- }
-
/* Open the file. */
h = CreateFileA (path, GENERIC_READ, FILE_SHARE_READ, &sec_none_nih, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0);
-
-syscall_printf ("opened '%s'(%p)", path, h);
-
res = -1;
if (h == INVALID_HANDLE_VALUE)
- __seterrno ();
+ goto file_not_symlink;
else
{
char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
DWORD got;
-syscall_printf ("ReadFile");
if (! ReadFile (h, cookie_buf, sizeof (cookie_buf), &got, 0))
- set_errno (EIO);
+ error = EIO;
else if (got == sizeof (cookie_buf)
&& memcmp (cookie_buf, SYMLINK_COOKIE,
sizeof (cookie_buf)) == 0)
{
/* It's a symlink. */
- *pflags = PATH_SYMLINK;
+ pflags = PATH_SYMLINK;
- res = ReadFile (h, buf, buflen, &got, 0);
+ res = ReadFile (h, contents, MAX_PATH + 1, &got, 0);
if (!res)
- set_errno (EIO);
+ error = EIO;
else
{
/* Versions prior to b16 stored several trailing
NUL. The length returned is the path without
*any* trailing NULs. We also have to handle (or
at least not die from) corrupted paths. */
- if (memchr (buf, 0, got) != NULL)
- res = strlen (buf);
+ if (memchr (contents, 0, got) != NULL)
+ res = strlen (contents);
else
res = got;
}
&& memcmp (cookie_buf, SOCKET_COOKIE,
sizeof (cookie_buf)) == 0)
{
- *pflags |= PATH_SOCKET;
+ pflags |= PATH_SOCKET;
goto close_and_return;
}
else
{
/* Not a symlink, see if executable. */
- if (!(*pflags & PATH_EXEC) && got >= 2 &&
+ if (!(pflags & PATH_ALL_EXEC) && got >= 2 &&
((cookie_buf[0] == '#' && cookie_buf[1] == '!') ||
- (cookie_buf[0] == ':' && cookie_buf[1] == '\n')))
- *pflags |= PATH_EXEC;
+ (cookie_buf[0] == ':' && cookie_buf[1] == '\n') ||
+ (cookie_buf[0] == 'M' && cookie_buf[1] == 'Z')))
+ pflags |= PATH_EXEC;
close_and_return:
-syscall_printf ("close_and_return");
CloseHandle (h);
goto file_not_symlink;
}
}
-syscall_printf ("breaking from loop");
CloseHandle (h);
break;
}
goto out;
file_not_symlink:
- set_errno (EINVAL);
+ is_symlink = FALSE;
syscall_printf ("not a symlink");
- if (ext_here)
- strcpy (buf, ext_here);
res = 0;
out:
- syscall_printf ("%d = symlink_check_one (%s, %p, %d) (%p)",
- res, path, buf, buflen, *pflags);
+ syscall_printf ("%d = symlink.check (%s, %p) (%p)",
+ res, path, contents, pflags);
return res;
}
int
readlink (const char *path, char *buf, int buflen)
{
- path_conv pathbuf (path, SYMLINK_CONTENTS);
+ extern suffix_info stat_suffixes[];
+
+ if (buflen < 0)
+ {
+ set_errno (ENAMETOOLONG);
+ return -1;
+ }
+
+ path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
+
if (pathbuf.error)
{
set_errno (pathbuf.error);
return -1;
}
- int len = strlen (pathbuf.get_win32 ());
- if (len > (buflen - 1))
- {
- set_errno (ENAMETOOLONG);
- return -1;
- }
+ int len = min (buflen, (int) strlen (pathbuf.get_win32 ()));
memcpy (buf, pathbuf.get_win32 (), len);
- buf[len] = '\0';
- /* errno set by symlink_check_one if error */
+ /* errno set by symlink.check if error */
return len;
}
{
char *nn, *newname = (char *) alloca (strlen (name) + 2);
nn = strncpy (newname, name, 2);
- if (islower (*nn))
- *newname = toupper (*nn);
+ if (isupper (*nn))
+ *newname = tolower (*nn);
*(nn += 2) = '\0';
name += 2;
if (*name != '\\')
Otherwise the inodes same will differ depending on whether a file is
referenced with an absolute value or relatively. */
- if (*name != '\\' && (current_directory_name == NULL ||
- get_current_directory_name ()))
+ if (!hash && !isabspath (name))
{
- hash = current_directory_hash;
+ hash = cygcwd.get_hash ();
if (name[0] == '.' && name[1] == '\0')
return hash;
- hash = hash_path_name (hash, "\\");
+ hash += hash_path_name (hash, "\\");
}
}
\a\b\. but allow a single \ if that's all there is. */
do
{
- hash += *name + (*name << 17);
+ int ch = tolower(*name);
+ hash += ch + (ch << 17);
hash ^= hash >> 2;
}
while (*++name != '\0' &&
return hash;
}
-static int
-get_current_directory_name ()
-{
- DWORD dlen, len;
-
- for (dlen = 256; ; dlen *= 2)
- {
- current_directory_name = (char *) realloc (current_directory_name, dlen + 2);
- if ((len = GetCurrentDirectoryA (dlen, current_directory_name)) < dlen)
- break;
- }
-
- if (len == 0)
- __seterrno ();
- else
- current_directory_hash = hash_path_name (0, current_directory_name);
-
- return len;
-}
-
-/* getcwd */
-
-char *
-getcwd_inner (char *buf, size_t ulen, int posix_p)
-{
- char *resbuf = NULL;
- size_t len = ulen;
-
- if (current_directory_name == NULL && !get_current_directory_name ())
- return NULL;
-
- if (!posix_p)
- {
- if (strlen (current_directory_name) >= len)
- set_errno (ERANGE);
- else
- {
- strcpy (buf, current_directory_name);
- resbuf = buf;
- }
-
- syscall_printf ("%p (%s) = getcwd_inner (%p, %d, win32) (cached)",
- resbuf, resbuf ? resbuf : "", buf, len);
- return resbuf;
- }
- else if (current_directory_posix_name != NULL)
- {
- if (strlen (current_directory_posix_name) >= len)
- set_errno (ERANGE);
- else
- {
- strcpy (buf, current_directory_posix_name);
- resbuf = buf;
- }
-
- syscall_printf ("%p (%s) = getcwd_inner (%p, %d, posix) (cached)",
- resbuf, resbuf ? resbuf : "", buf, len);
- return resbuf;
- }
-
- /* posix_p required and current_directory_posix_name == NULL */
-
- char temp[MAX_PATH];
-
- /* Turn from Win32 style to our style. */
- cygwin_shared->mount.conv_to_posix_path (current_directory_name, temp, 0);
-
- size_t tlen = strlen (temp);
-
- current_directory_posix_name = (char *) realloc (
- current_directory_posix_name, tlen + 1);
- if (current_directory_posix_name != NULL)
- strcpy (current_directory_posix_name, temp);
-
- if (tlen >= ulen)
- {
- /* len was too small */
- set_errno (ERANGE);
- }
- else
- {
- strcpy (buf, temp);
- resbuf = buf;
- }
-
- syscall_printf ("%p (%s) = getcwd_inner (%p, %d, %s)",
- resbuf, resbuf ? resbuf : "",
- buf, len, posix_p ? "posix" : "win32");
- return resbuf;
-}
-
char *
getcwd (char *buf, size_t ulen)
{
- char *res;
-
- if (buf == NULL || ulen == 0)
- {
- buf = (char *) alloca (MAX_PATH);
- res = getcwd_inner (buf, MAX_PATH, 1);
- res = strdup (buf);
- }
- else
- {
- res = getcwd_inner (buf, ulen, 1);
- }
-
- return res;
+ return cygcwd.get (buf, 1, 1, ulen);
}
/* getwd: standards? */
int
chdir (const char *dir)
{
- path_conv path (dir);
+ MALLOC_CHECK;
+ syscall_printf ("dir %s", dir);
+ path_conv path (dir, PC_FULL | PC_SYM_FOLLOW);
if (path.error)
{
return -1;
}
+ /* Look for trailing path component consisting entirely of dots. This
+ is needed only in case of chdir since Windows simply ignores count
+ of dots > 2 here instead of returning an error code. Counts of dots
+ <= 2 are already eliminated by normalize_posix_path. */
+ const char *p = strrchr (dir, '/');
+ if (!p)
+ p = dir;
+ else
+ p++;
+
+ size_t len = strlen (p);
+ if (len > 2 && strspn (p, ".") == len)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
char *native_dir = path.get_win32 ();
/* 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 (isalpha (native_dir[0]) && native_dir[1] == ':' && !native_dir[2])
+ if (isdrive (native_dir) && !native_dir[2])
{
native_dir[2] = '\\';
native_dir[3] = '\0';
}
- int res = SetCurrentDirectoryA (native_dir);
- if (!res)
+ int res = SetCurrentDirectoryA (native_dir) ? 0 : -1;
+ if (res == -1)
__seterrno ();
+ else
+ cygcwd.set (path, dir);
- /* Clear the cache until we need to retrieve the directory again. */
- if (current_directory_name != NULL)
- {
- free (current_directory_name);
- current_directory_name = NULL;
- }
- if (current_directory_posix_name != NULL)
- {
- free (current_directory_posix_name);
- current_directory_posix_name = NULL;
- }
-
- syscall_printf ("%d = chdir (%s) (dos %s)", res ? 0 : -1, dir, native_dir);
- return res ? 0 : -1;
+ /* 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() cygcwd.posix '%s' native '%s'", res,
+ cygcwd.posix, native_dir);
+ MALLOC_CHECK;
+ return res;
}
/******************** Exported Path Routines *********************/
int
cygwin_conv_to_win32_path (const char *path, char *win32_path)
{
- path_conv p (path, SYMLINK_FOLLOW, 0);
+ path_conv p (path, PC_SYM_FOLLOW);
if (p.error)
{
set_errno (p.error);
int
cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
{
- path_conv p (path, SYMLINK_FOLLOW, 1);
+ path_conv p (path, PC_SYM_FOLLOW | PC_FULL);
if (p.error)
{
set_errno (p.error);
{
int err;
- path_conv real_path (path, SYMLINK_FOLLOW, 1);
+ path_conv real_path (path, PC_SYM_FOLLOW | PC_FULL);
if (real_path.error)
err = real_path.error;
int
cygwin_posix_path_list_p (const char *path)
{
- int posix_p = ! (strchr (path, ';')
- || (isalpha (path[0]) && path[1] == ':'));
+ int posix_p = ! (strchr (path, ';') || isdrive (path));
return posix_p;
}
/* Deal with drives.
Remember that c:foo <==> c:/foo. */
- if (isalpha (path[0]) && path[1] == ':')
+ if (isdrive (path))
{
*dir++ = *path++;
*dir++ = *path++;
(x != CHXOR || !isalpha (ch1))); \
})
-int __stdcall
+/* Return TRUE if two strings match up to length n */
+extern "C" int __stdcall
strncasematch (const char *s1, const char *s2, size_t n)
{
if (s1 == s2)
return !n || *s2 == '\0';
}
-int __stdcall
+/* Return TRUE if two strings match */
+extern "C" int __stdcall
strcasematch (const char *s1, const char *s2)
{
if (s1 == s2)
return *s2 == '\0';
}
-char * __stdcall
+extern "C" char * __stdcall
strcasestr (const char *searchee, const char *lookfor)
{
if (*searchee == 0)
return NULL;
}
+
+int __stdcall
+check_null_empty_path (const char *name)
+{
+ MEMORY_BASIC_INFORMATION m;
+ if (!name || !VirtualQuery (name, &m, sizeof (m)) || (m.State != MEM_COMMIT))
+ return EFAULT;
+
+ if (!*name)
+ return ENOENT;
+
+ return 0;
+}
+
+/* Return the hash value for the current win32 value.
+ This is used when constructing inodes. */
+DWORD
+cwdstuff::get_hash ()
+{
+ DWORD hashnow;
+ lock->acquire ();
+ hashnow = hash;
+ lock->release ();
+ return hashnow;
+}
+
+/* Initialize cygcwd 'muto' for serializing access to cwd info. */
+void
+cwdstuff::init ()
+{
+ lock = new_muto (FALSE, "cwd");
+}
+
+/* Called to fill in cwd values after an exec. */
+void
+cwdstuff::fixup_after_exec (char *win32_cwd, char *posix_cwd, DWORD hash_cwd)
+{
+ win32 = win32_cwd;
+ posix = posix_cwd;
+ hash = hash_cwd;
+}
+
+/* Get initial cwd. Should only be called once in a
+ process tree. */
+bool
+cwdstuff::get_initial ()
+{
+ lock->acquire ();
+
+ if (win32)
+ return 1;
+
+ int i;
+ DWORD len, dlen;
+ for (i = 0, dlen = MAX_PATH, len = 0; i < 3; dlen *= 2, i++)
+ {
+ win32 = (char *) crealloc (win32, dlen + 2);
+ if ((len = GetCurrentDirectoryA (dlen, win32)) < dlen)
+ break;
+ }
+
+ if (len == 0)
+ {
+ __seterrno ();
+ lock->release ();
+ debug_printf ("get_initial_cwd failed, %E");
+ lock->release ();
+ return 0;
+ }
+ set (NULL);
+ return 1; /* Leaves cwd lock unreleased */
+}
+
+/* Fill out the elements of a cwdstuff struct.
+ It is assumed that the lock for the cwd is acquired if
+ win32_cwd == NULL. */
+void
+cwdstuff::set (const char *win32_cwd, const char *posix_cwd)
+{
+ char pathbuf[MAX_PATH];
+
+ if (win32_cwd)
+ {
+ lock->acquire ();
+ win32 = (char *) crealloc (win32, strlen (win32_cwd) + 1);
+ strcpy (win32, win32_cwd);
+ }
+
+ if (!posix_cwd)
+ cygwin_shared->mount.conv_to_posix_path (win32, pathbuf, 0);
+ else
+ (void) normalize_posix_path (posix_cwd, pathbuf);
+
+ posix = (char *) crealloc (posix, strlen (pathbuf) + 1);
+ strcpy (posix, pathbuf);
+
+ hash = hash_path_name (0, win32);
+
+ if (win32_cwd)
+ lock->release ();
+
+ return;
+}
+
+/* Copy the value for either the posix or the win32 cwd into a buffer. */
+char *
+cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
+{
+ MALLOC_CHECK;
+
+ if (ulen == 0)
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+
+ if (!get_initial ()) /* Get initial cwd and set cwd lock */
+ return NULL;
+
+ char *tocopy;
+ if (!need_posix)
+ tocopy = win32;
+ else
+ tocopy = with_chroot && ischrootpath(posix) ?
+ posix + myself->rootlen : posix;
+
+ debug_printf("myself->root: %s, posix: %s", myself->root, posix);
+ if (strlen (tocopy) >= ulen)
+ {
+ set_errno (ERANGE);
+ buf = NULL;
+ }
+ else
+ {
+ if (!buf)
+ buf = (char *) malloc (strlen (tocopy) + 1);
+ strcpy (buf, tocopy);
+ if (!buf[0]) /* Should only happen when chroot */
+ strcpy (buf, "/");
+ }
+
+ lock->release ();
+
+out:
+ syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
+ buf, buf, ulen, need_posix, with_chroot, errno);
+ MALLOC_CHECK;
+ return buf;
+}
+
+/* Get copies of all cwdstuff elements. Used by spawn_guts. */
+void
+cwdstuff::copy (char * &posix_cwd, char * &win32_cwd, DWORD hash_cwd)
+{
+ lock->acquire ();
+ get_initial (); /* FIXME: Check return someday */
+ posix_cwd = cstrdup (posix);
+ win32_cwd = cstrdup (win32);
+ hash_cwd = hash;
+ MALLOC_CHECK;
+ lock->release ();
+}