diff options
Diffstat (limited to 'winsup/cygwin/path.cc')
-rw-r--r-- | winsup/cygwin/path.cc | 404 |
1 files changed, 206 insertions, 198 deletions
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index c218a1606..a0e8640a1 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -93,19 +93,18 @@ static int path_prefix_p_ (const char *path1, const char *path2, int len1); struct symlink_info { - char buf[3 + MAX_PATH * 3]; + char contents[MAX_PATH + 4]; char *ext_here; int extn; - char *contents; unsigned pflags; DWORD fileattr; int is_symlink; bool ext_tacked_on; int error; BOOL case_clash; - symlink_info (): contents (buf + MAX_PATH + 1) {} int check (const char *path, const suffix_info *suffixes, - char *orig_path, BOOL sym_ignore); + char *orig_path, unsigned opt, + DWORD& devn, int& unit, unsigned& path_flags); BOOL case_check (const char *path, char *orig_path); }; @@ -178,6 +177,123 @@ pathmatch (const char *path1, const char *path2) : strcasematch (path1, path2); } +/* Normalize a POSIX path. + \'s are converted to /'s in the process. + All duplicate /'s, except for 2 leading /'s, are deleted. + The result is 0 for success, or an errno error value. */ + +#define isslash(c) ((c) == '/') + +static int +normalize_posix_path (const char *src, char *dst) +{ + const char *src_start = src; + char *dst_start = dst; + + 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 (!cygheap->cwd.get (dst)) + return get_errno (); + dst = strchr (dst, '\0'); + if (*src == '.') + { + if (dst == dst_start + 1 && *dst_start == '/') + --dst; + goto sawdot; + } + if (dst > dst_start && !isslash (dst[-1])) + *dst++ = '/'; + } + /* Two leading /'s? If so, preserve them. */ + else if (isslash (src[1])) + { + if (cygheap->root.length ()) + { + debug_printf ("ENOENT = normalize_posix_path (%s)", src); + return ENOENT; + } + *dst++ = '/'; + *dst++ = '/'; + src += 2; + 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 (cygheap->root.length ()) + { + strcpy (dst, cygheap->root.path ()); + dst += cygheap->root.length (); + } + else + *dst = '\0'; + + while (*src) + { + /* Strip runs of /'s. */ + if (!isslash (*src)) + *dst++ = *src++; + else + { + 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) cygheap->root.length ()) + while (dst > dst_start && !isslash (*--dst)) + continue; + src++; + } + } + + *dst++ = '/'; + } + if ((dst - dst_start) >= MAX_PATH) + { + debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src); + return ENAMETOOLONG; + } + } + +done: + *dst = '\0'; + if (--dst > dst_start && isslash (*dst)) + *dst = '\0'; + + debug_printf ("%s = normalize_posix_path (%s)", dst_start, src_start); + return 0; +} + inline void path_conv::add_ext_from_sym (symlink_info &sym) { @@ -209,9 +325,8 @@ path_conv::check (const char *src, unsigned opt, /* 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 path_buf[MAX_PATH]; - char path_copy[MAX_PATH]; - char tmp_buf[MAX_PATH]; + char path_copy[MAX_PATH + 3]; + char tmp_buf[2 * MAX_PATH + 3]; symlink_info sym; bool need_directory = 0; bool saw_symlinks = 0; @@ -227,8 +342,6 @@ path_conv::check (const char *src, unsigned opt, } #endif - char *rel_path, *full_path; - int loop = 0; path_flags = 0; known_suffix = NULL; @@ -241,11 +354,6 @@ path_conv::check (const char *src, unsigned opt, else if ((error = check_null_empty_path (src))) return; - if (opt & PC_FULL) - rel_path = path_buf, full_path = this->path; - else - rel_path = this->path, full_path = path_buf; - /* This loop handles symlink expansion. */ for (;;) { @@ -260,39 +368,14 @@ path_conv::check (const char *src, unsigned opt, else if ((p = strrchr (src, '\\')) && (p[1] == '\0' || strcmp (p, "\\.") == 0)) need_directory = 1; - /* Must look up path in mount table, etc. */ - error = mount_table->conv_to_win32_path (src, rel_path, full_path, devn, - unit, &path_flags); - MALLOC_CHECK; + + error = normalize_posix_path (src, path_copy); if (error) return; - if (devn != FH_BAD) - { - fileattr = 0; - return; - } - /* Eat trailing slashes */ - char *tail = strchr (full_path, '\0'); - /* If path is only a drivename, Windows interprets it as - the current working directory on this drive instead of - the root dir which is what we want. So we need - the trailing backslash in this case. */ - while (tail > full_path + 3 && (*--tail == '\\')) - *tail = '\0'; - if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0') - strcat (full_path, "\\"); - - if ((opt & PC_SYM_IGNORE) && pcheck_case == PCHECK_RELAXED) - { - fileattr = GetFileAttributesA (path); - goto out; - } - - /* Make a copy of the path that we can munge up */ - strcpy (path_copy, full_path); - - tail = path_copy + 1 + (tail - full_path); // Point to end of copy + char *tail = strchr (path_copy, '\0'); // Point to end of copy + char *path_end = tail; + tail[1] = '\0'; /* Scan path_copy from right to left looking either for a symlink or an actual existing file. If an existing file is found, just @@ -306,6 +389,8 @@ path_conv::check (const char *src, unsigned opt, for (;;) { const suffix_info *suff; + char pathbuf[MAX_PATH]; + char *full_path; /* Don't allow symlink.check to set anything in the path_conv class if we're working on an inner component of the path */ @@ -313,14 +398,17 @@ path_conv::check (const char *src, unsigned opt, { suff = NULL; sym.pflags = 0; + full_path = pathbuf; } else { suff = suffixes; sym.pflags = path_flags; + full_path = this->path; } - int len = sym.check (path_copy, suff, full_path, opt & PC_SYM_IGNORE); + int len = sym.check (path_copy, suff, full_path, opt, + devn, unit, path_flags); if (sym.case_clash) { @@ -391,10 +479,15 @@ path_conv::check (const char *src, unsigned opt, } - if (!(tail = strrchr (path_copy, '\\')) || - (tail > path_copy && tail[-1] == ':')) + char *newtail = strrchr (path_copy, '/'); + if (tail != path_end) + *tail = '/'; + + if (!newtail) goto out; // all done + tail = newtail; + /* Haven't found an existing pathname component yet. Pinch off the tail and try again. */ *tail = '\0'; @@ -407,10 +500,10 @@ path_conv::check (const char *src, unsigned opt, error = ELOOP; // Eep. return; } + MALLOC_CHECK; - tail = full_path + (tail - path_copy); - int taillen = strlen (tail); + int taillen = strlen (tail + 1); int buflen = strlen (sym.contents); if (buflen + taillen > MAX_PATH) { @@ -419,31 +512,33 @@ path_conv::check (const char *src, unsigned opt, return; } - /* Copy tail of full_path to discovered symlink. */ - for (p = sym.contents + buflen; *tail; tail++) - *p++ = *tail == '\\' ? '/' : *tail; + if ((p = strrchr (path_copy, '/')) == NULL) + p = path_copy; *p = '\0'; - /* If symlink referred to an absolute path, then we - 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. */ + char *headptr; if (isabspath (sym.contents)) - src = sym.contents; - else if (!(tail = strrchr (path_copy, '\\'))) - system_printf ("problem parsing %s - '%s'", src, full_path); + headptr = tmp_buf; else { - int headlen = 1 + tail - path_copy; - p = sym.contents - headlen; - memcpy (p, path_copy, headlen); - MALLOC_CHECK; - error = mount_table->conv_to_posix_path (p, tmp_buf, 1); - MALLOC_CHECK; - if (error) - return; - src = tmp_buf; + strcpy (tmp_buf, path_copy); + headptr = strchr (tmp_buf, '\0'); } + + if (headptr > tmp_buf && headptr[-1] != '/') + *headptr++ = '/'; + + for (p = sym.contents; *p; p++) + *headptr++ = *p == '\\' ? '/' : *p; + if (tail == path_end) + *headptr = '\0'; + else + { + *headptr++ = '/'; + strcpy (headptr, tail); + } + + src = tmp_buf; } /*fillin:*/ @@ -467,21 +562,21 @@ out: DWORD serial, volflags; char fs_name[16]; - strcpy (tmp_buf, full_path); + strcpy (tmp_buf, this->path); if (!rootdir (tmp_buf) || !GetVolumeInformation (tmp_buf, NULL, 0, &serial, NULL, &volflags, fs_name, 16)) { - debug_printf ("GetVolumeInformation(%s) = ERR, full_path(%s), set_has_acls(FALSE)", - tmp_buf, full_path, GetLastError ()); + debug_printf ("GetVolumeInformation(%s) = ERR, this->path(%s), set_has_acls(FALSE)", + tmp_buf, this->path, GetLastError ()); set_has_acls (FALSE); set_has_buggy_open (FALSE); } else { set_isdisk (); - debug_printf ("GetVolumeInformation(%s) = OK, full_path(%s), set_has_acls(%d)", - tmp_buf, full_path, volflags & FS_PERSISTENT_ACLS); + debug_printf ("GetVolumeInformation(%s) = OK, this->path(%s), set_has_acls(%d)", + tmp_buf, this->path, volflags & FS_PERSISTENT_ACLS); if (!allow_smbntsec && ((tmp_buf[0] == '\\' && tmp_buf[1] == '\\') || GetDriveType (tmp_buf) == DRIVE_REMOTE)) @@ -594,7 +689,7 @@ get_device_number (const char *name, int &unit, BOOL from_conv) name += 5; if (deveq ("tty")) { - if (tty_attached (myself)) + if (real_tty_attached (myself)) { unit = myself->ctty; devn = FH_TTYS; @@ -683,123 +778,6 @@ win32_device_name (const char *src_path, char *win32_path, return TRUE; } -/* Normalize a POSIX path. - \'s are converted to /'s in the process. - All duplicate /'s, except for 2 leading /'s, are deleted. - The result is 0 for success, or an errno error value. */ - -#define isslash(c) ((c) == '/') - -static int -normalize_posix_path (const char *src, char *dst) -{ - const char *src_start = src; - char *dst_start = dst; - - 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 (!cygheap->cwd.get (dst)) - return get_errno (); - dst = strchr (dst, '\0'); - if (*src == '.') - { - if (dst == dst_start + 1 && *dst_start == '/') - --dst; - goto sawdot; - } - if (dst > dst_start && !isslash (dst[-1])) - *dst++ = '/'; - } - /* Two leading /'s? If so, preserve them. */ - else if (isslash (src[1])) - { - if (cygheap->root.length ()) - { - debug_printf ("ENOENT = normalize_posix_path (%s)", src); - return ENOENT; - } - *dst++ = '/'; - *dst++ = '/'; - src += 2; - 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 (cygheap->root.length ()) - { - strcpy (dst, cygheap->root.path ()); - dst += cygheap->root.length (); - } - else - *dst = '\0'; - - while (*src) - { - /* Strip runs of /'s. */ - if (!isslash (*src)) - *dst++ = *src++; - else - { - 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) cygheap->root.length ()) - while (dst > dst_start && !isslash (*--dst)) - continue; - src++; - } - } - - *dst++ = '/'; - } - if ((dst - dst_start) >= MAX_PATH) - { - debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src); - return ENAMETOOLONG; - } - } - -done: - *dst = '\0'; - if (--dst > dst_start && isslash (*dst)) - *dst = '\0'; - - debug_printf ("%s = normalize_posix_path (%s)", dst_start, src_start); - return 0; -} - /* Normalize a Win32 path. /'s are converted to \'s in the process. All duplicate \'s, except for 2 leading \'s, are deleted. @@ -2643,15 +2621,45 @@ suffix_scan::next () int symlink_info::check (const char *path, const suffix_info *suffixes, - char *orig_path, BOOL sym_ignore) + char *full_path, unsigned opt, + DWORD& devn, int& unit, unsigned& path_flags) { HANDLE h; int res = 0; suffix_scan suffix; + contents[0] = '\0'; + char *tail; + + error = mount_table->conv_to_win32_path (path, NULL, full_path, devn, + unit, &path_flags); + + if (devn != FH_BAD) + { + fileattr = 0; + goto out; /* Found a device. Stop parsing. */ + } + + /* Eat trailing slashes */ + tail = strchr (full_path, '\0'); + + /* If path is only a drivename, Windows interprets it as the current working + directory on this drive instead of the root dir which is what we want. So + we need the trailing backslash in this case. */ + while (tail > full_path + 3 && (*--tail == '\\')) + *tail = '\0'; + + if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0') + strcat (full_path, "\\"); + + if ((opt & PC_SYM_IGNORE) && pcheck_case == PCHECK_RELAXED) + { + fileattr = GetFileAttributesA (path); + goto out; + } is_symlink = TRUE; - ext_here = suffix.has (path, suffixes); - extn = ext_here - path; + ext_here = suffix.has (full_path, suffixes); + extn = ext_here - full_path; ext_tacked_on = !*ext_here; @@ -2671,8 +2679,8 @@ symlink_info::check (const char *path, const suffix_info *suffixes, continue; } - if (pcheck_case != PCHECK_RELAXED && !case_check (path, orig_path) - || sym_ignore) + if (pcheck_case != PCHECK_RELAXED && !case_check (path, full_path) + || (opt & PC_SYM_IGNORE)) goto file_not_symlink; int sym_check = 0; @@ -2684,13 +2692,13 @@ symlink_info::check (const char *path, const suffix_info *suffixes, if (suffix.lnk_match ()) sym_check = 1; - /* The old Cygwin method creating symlinks: */ + /* This is the old Cygwin method creating symlinks: */ /* A symlink will have the `system' file attribute. */ /* Only files can be symlinks (which can be symlinks to directories). */ if (fileattr & FILE_ATTRIBUTE_SYSTEM) sym_check = 2; - if (!sym_check && !(pflags & PATH_SYMLINK)) + if (!sym_check) goto file_not_symlink; /* Open the file. */ |