From 70c370d674c5696d39e64970300c889c64ecd752 Mon Sep 17 00:00:00 2001 From: Corinna Vinschen Date: Thu, 12 Apr 2001 21:21:37 +0000 Subject: [PATCH] * dir.cc (mkdir): Check for case clash. * environ.cc: Add extern declaration for `pcheck_case'. (check_case_init): New function. (struct parse_thing): Add "check_case" option. * errno.cc (_sys_nerrlist): Add text for ECASECLASH. (strerror): Add case branch for ECASECLASH. * fhandler.cc (fhandler_disk_file::open): Check for case clash. * path.cc: Add global variable `pcheck_case'. (struct symlink_info): Add member `case_clash' and method `case_check'. (path_prefix_p_): Call `pathnmatch' instead of `strncasematch'. (pathnmatch): New funtion. (pathmatch): Ditto. (path_conv::check): Add handling for case checking. (symlink): Check for case clash. (symlink_info::check): Add parameter for case checking. Handle case checking. (symlink_info::case_check): New method. (chdir): Don't use unconverted path if pcheck_case==PCHECK_STRICT. * path.h: Add extern declarations for `pathmatch' and `pathnmatch'. (enum case_checking): New enumeration type describing the case checking behaviour of path conversion routines. (class path_conv): Add member `case_clash'. * syscalls.cc (_link): Check for case clash. --- winsup/cygwin/ChangeLog | 27 ++++++ winsup/cygwin/dir.cc | 2 +- winsup/cygwin/environ.cc | 29 ++++++ winsup/cygwin/errno.cc | 6 +- winsup/cygwin/fhandler.cc | 6 +- winsup/cygwin/path.cc | 189 ++++++++++++++++++++++++++++++-------- winsup/cygwin/path.h | 12 +++ winsup/cygwin/syscalls.cc | 2 +- 8 files changed, 228 insertions(+), 45 deletions(-) diff --git a/winsup/cygwin/ChangeLog b/winsup/cygwin/ChangeLog index 560dda555..d3d59bba5 100644 --- a/winsup/cygwin/ChangeLog +++ b/winsup/cygwin/ChangeLog @@ -1,3 +1,30 @@ +Thu Apr 17 23:19:00 2001 Corinna Vinschen + + * dir.cc (mkdir): Check for case clash. + * environ.cc: Add extern declaration for `pcheck_case'. + (check_case_init): New function. + (struct parse_thing): Add "check_case" option. + * errno.cc (_sys_nerrlist): Add text for ECASECLASH. + (strerror): Add case branch for ECASECLASH. + * fhandler.cc (fhandler_disk_file::open): Check for case clash. + * path.cc: Add global variable `pcheck_case'. + (struct symlink_info): Add member `case_clash' and method `case_check'. + (path_prefix_p_): Call `pathnmatch' instead of `strncasematch'. + (pathnmatch): New funtion. + (pathmatch): Ditto. + (path_conv::check): Add handling for case checking. + (symlink): Check for case clash. + (symlink_info::check): Add parameter for case checking. + Handle case checking. + (symlink_info::case_check): New method. + (chdir): Don't use unconverted path if pcheck_case==PCHECK_STRICT. + * path.h: Add extern declarations for `pathmatch' and + `pathnmatch'. + (enum case_checking): New enumeration type describing + the case checking behaviour of path conversion routines. + (class path_conv): Add member `case_clash'. + * syscalls.cc (_link): Check for case clash. + Thu Apr 12 12:49:53 2001 Christopher Faylor * syscalls.cc (mkfifo): New function stub. diff --git a/winsup/cygwin/dir.cc b/winsup/cygwin/dir.cc index fef678756..2f13fcdca 100644 --- a/winsup/cygwin/dir.cc +++ b/winsup/cygwin/dir.cc @@ -306,7 +306,7 @@ mkdir (const char *dir, mode_t mode) if (real_dir.error) { - set_errno (real_dir.error); + set_errno (real_dir.case_clash ? ECASECLASH : real_dir.error); goto done; } diff --git a/winsup/cygwin/environ.cc b/winsup/cygwin/environ.cc index 43b246e27..2f8e350d8 100644 --- a/winsup/cygwin/environ.cc +++ b/winsup/cygwin/environ.cc @@ -32,6 +32,7 @@ extern BOOL allow_ntea; extern BOOL allow_smbntsec; extern BOOL allow_winsymlinks; extern BOOL strip_title_path; +extern int pcheck_case; extern DWORD chunksize; BOOL reset_com = TRUE; static BOOL envcache = TRUE; @@ -393,6 +394,33 @@ glob_init (const char *buf) } } +static void +check_case_init (const char *buf) +{ + if (!buf || !*buf) + return; + + if (strncmp (buf, "relax", 5)== 0) + { + pcheck_case = PCHECK_RELAXED; + debug_printf ("File case checking set to RELAXED"); + } + else if (strcmp (buf, "adjust")== 0) + { + pcheck_case = PCHECK_ADJUST; + debug_printf ("File case checking set to ADJUST"); + } + else if (strcmp (buf, "strict")== 0) + { + pcheck_case = PCHECK_STRICT; + debug_printf ("File case checking set to STRICT"); + } + else + { + debug_printf ("Wrong case checking name: %s", buf); + } +} + static void codepage_init (const char *buf) { @@ -441,6 +469,7 @@ struct parse_thing } known[] = { {"binmode", {x: &binmode}, justset, NULL, {{O_TEXT}, {O_BINARY}}}, + {"check_case", {func: &check_case_init}, isfunc, NULL, {{0}, {0}}}, {"codepage", {func: &codepage_init}, isfunc, NULL, {{0}, {0}}}, {"envcache", {&envcache}, justset, NULL, {{TRUE}, {FALSE}}}, {"error_start", {func: &error_start_init}, isfunc, NULL, {{0}, {0}}}, diff --git a/winsup/cygwin/errno.cc b/winsup/cygwin/errno.cc index c7b491cb4..3a4026077 100644 --- a/winsup/cygwin/errno.cc +++ b/winsup/cygwin/errno.cc @@ -283,7 +283,8 @@ extern const char __declspec(dllexport) * const _sys_errlist[]= /* ESTALE 133 */ "Stale NFS file handle", /* ENOTSUP 134 */ "134", /* ENOMEDIUM 135 */ "no medium", -/* ENOSHARE 136 */ "No such host or network path" +/* ENOSHARE 136 */ "No such host or network path", +/* ECASECLASH 137 */ "Filename exists with different case" }; int __declspec(dllexport) _sys_nerr = @@ -659,6 +660,9 @@ strerror (int errnum) case ENOSHARE: error = "No such host or network path"; break; + case ECASECLASH: + error = "Filename exists with different case"; + break; default: #ifdef _MT_SAFE char *buf= _reent_winsup()->_strerror_buf; diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc index 73fe3f554..ff9a9dfb4 100644 --- a/winsup/cygwin/fhandler.cc +++ b/winsup/cygwin/fhandler.cc @@ -1208,9 +1208,11 @@ fhandler_disk_file::open (const char *path, int flags, mode_t mode) PC_SYM_NOFOLLOW : PC_SYM_FOLLOW); if (real_path.error && - (flags & O_NOSYMLINK || real_path.error != ENOENT || !(flags & O_CREAT))) + (flags & O_NOSYMLINK || real_path.error != ENOENT + || !(flags & O_CREAT) || real_path.case_clash)) { - set_errno (real_path.error); + set_errno (flags & O_CREAT && real_path.case_clash ? ECASECLASH + : real_path.error); syscall_printf ("0 = fhandler_disk_file::open (%s, %p)", path, flags); return 0; } diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc index 9d2f63d54..3193c7a98 100644 --- a/winsup/cygwin/path.cc +++ b/winsup/cygwin/path.cc @@ -101,12 +101,17 @@ struct symlink_info 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); + int check (const char *path, const suffix_info *suffixes, + char *orig_path, BOOL sym_ignore); + BOOL case_check (const char *path, char *orig_path); }; cwdstuff cygcwd; /* The current working directory. */ +int pcheck_case = PCHECK_RELAXED; /* Determines the case check behaviour. */ + #define path_prefix_p(p1, p2, l1) \ ((cyg_tolower(*(p1))==cyg_tolower(*(p2))) && \ path_prefix_p_(p1, p2, l1)) @@ -150,12 +155,30 @@ path_prefix_p_ (const char *path1, const char *path2, int len1) if (len1 == 0) return SLASH_P (path2[0]) && !SLASH_P (path2[1]); - if (!strncasematch (path1, path2, len1)) + if (!pathnmatch (path1, path2, len1)) return 0; return SLASH_P (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':'; } +/* Return non-zero if paths match in first len chars. + Check is dependent of the case sensitivity setting. */ +int +pathnmatch (const char *path1, const char *path2, int len) +{ + return pcheck_case == PCHECK_STRICT ? !strncmp (path1, path2, len) + : strncasematch (path1, path2, len); +} + +/* Return non-zero if paths match. Check is dependent of the case + sensitivity setting. */ +int +pathmatch (const char *path1, const char *path2) +{ + return pcheck_case == PCHECK_STRICT ? !strcmp (path1, path2) + : strcasematch (path1, path2); +} + /* Convert an arbitrary path SRC to a pure Win32 path, suitable for passing to Win32 API routines. @@ -211,6 +234,7 @@ path_conv::check (const char *src, unsigned opt, path_flags = 0; known_suffix = NULL; fileattr = (DWORD) -1; + case_clash = FALSE; for (;;) { MALLOC_CHECK; @@ -247,7 +271,7 @@ path_conv::check (const char *src, unsigned opt, if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0') strcat (full_path, "\\"); - if (opt & PC_SYM_IGNORE) + if ((opt & PC_SYM_IGNORE) && pcheck_case == PCHECK_RELAXED) { fileattr = GetFileAttributesA (path); goto out; @@ -284,44 +308,77 @@ path_conv::check (const char *src, unsigned opt, sym.pflags = path_flags; } - int len = sym.check (path_copy, suff); + int len = sym.check (path_copy, suff, full_path, opt & PC_SYM_IGNORE); - if (!component) - path_flags = sym.pflags; + if (sym.case_clash) + { + case_clash = TRUE; + error = ENOENT; + goto out; + } - /* 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) + if (!(opt & PC_SYM_IGNORE)) { - error = sym.error; - if (component == 0) + 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) { - fileattr = sym.fileattr; - goto fillin; + error = sym.error; + if (component == 0) + { + fileattr = sym.fileattr; + if (sym.ext_here && *sym.ext_here) + { + known_suffix = this->path + sym.extn; + if (sym.ext_tacked_on) + strcpy (known_suffix, sym.ext_here); + } + } + if (pcheck_case == PCHECK_RELAXED) + goto out; // file found + /* Avoid further symlink evaluation. Only case checks are + done now. */ + opt |= PC_SYM_IGNORE; } - goto out; // file found - } - /* Found a symlink if len > 0. If component == 0, then the - src path itself was a symlink. If !follow_mode then - we're done. Otherwise we have to insert the path found - into the full path that we are building and perform all of - these operations again on the newly derived path. */ - else if (len > 0) - { - saw_symlinks = 1; - if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW)) + /* Found a symlink if len > 0. If component == 0, then the + src path itself was a symlink. If !follow_mode then + we're done. Otherwise we have to insert the path found + into the full path that we are building and perform all of + these operations again on the newly derived path. */ + else if (len > 0) { - set_symlink (); // last component of path is a symlink. - fileattr = sym.fileattr; - if (opt & PC_SYM_CONTENTS) - strcpy (path, sym.contents); - goto fillin; + saw_symlinks = 1; + if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW)) + { + set_symlink (); // last component of path is a symlink. + fileattr = sym.fileattr; + if (opt & PC_SYM_CONTENTS) + { + strcpy (path, sym.contents); + goto out; + } + if (sym.ext_here && *sym.ext_here) + { + known_suffix = this->path + sym.extn; + if (sym.ext_tacked_on) + strcpy (known_suffix, sym.ext_here); + } + if (pcheck_case == PCHECK_RELAXED) + goto out; + /* Avoid further symlink evaluation. Only case checks are + done now. */ + opt |= PC_SYM_IGNORE; + } + else + break; } - break; - } + /* No existing file found. */ - /* No existing file found. */ + } if (!(tail = strrchr (path_copy, '\\')) || (tail > path_copy && tail[-1] == ':')) @@ -378,7 +435,7 @@ path_conv::check (const char *src, unsigned opt, } } -fillin: +/*fillin:*/ if (sym.ext_here && *sym.ext_here && !(opt & PC_SYM_CONTENTS)) { known_suffix = this->path + sym.extn; @@ -399,6 +456,7 @@ out: error = ENOTDIR; return; } + DWORD serial, volflags; char fs_name[16]; @@ -2258,18 +2316,17 @@ symlink (const char *topath, const char *frompath) char w32topath[MAX_PATH + 1]; DWORD written; - if (allow_winsymlinks) + win32_path.check (frompath, PC_SYM_NOFOLLOW); + if (allow_winsymlinks && !win32_path.error) { strcpy (from, frompath); strcat (from, ".lnk"); win32_path.check (from, PC_SYM_NOFOLLOW); } - else - win32_path.check (frompath, PC_SYM_NOFOLLOW); if (win32_path.error) { - set_errno (win32_path.error); + set_errno (win32_path.case_clash ? ECASECLASH : win32_path.error); goto done; } @@ -2573,7 +2630,8 @@ suffix_scan::next () stored into BUF if PATH is a symlink. */ int -symlink_info::check (const char *path, const suffix_info *suffixes) +symlink_info::check (const char *path, const suffix_info *suffixes, + char *orig_path, BOOL sym_ignore) { HANDLE h; int res = 0; @@ -2585,6 +2643,8 @@ symlink_info::check (const char *path, const suffix_info *suffixes) ext_tacked_on = !*ext_here; + case_clash = FALSE; + while (suffix.next ()) { error = 0; @@ -2599,6 +2659,10 @@ symlink_info::check (const char *path, const suffix_info *suffixes) continue; } + if (pcheck_case != PCHECK_RELAXED && !case_check (path, orig_path) + || sym_ignore) + goto file_not_symlink; + int sym_check = 0; if (fileattr & FILE_ATTRIBUTE_DIRECTORY) @@ -2662,6 +2726,50 @@ out: return res; } +/* Check the correct case of the last path component (given in DOS style). + Adjust the case in this->path if pcheck_case == PCHECK_ADJUST or return + FALSE if pcheck_case == PCHECK_STRICT. + Dont't call if pcheck_case == PCHECK_RELAXED. +*/ + +BOOL +symlink_info::case_check (const char *path, char *orig_path) +{ + WIN32_FIND_DATA data; + HANDLE h; + const char *c; + + /* Set a pointer to the beginning of the last component. */ + if (!(c = strrchr (path, '\\'))) + c = path; + else + ++c; + + if ((h = FindFirstFile (path, &data)) + != INVALID_HANDLE_VALUE) + { + FindClose (h); + + /* If that part of the component exists, check the case. */ + if (strcmp (c, data.cFileName)) + { + /* If check is set to STRICT, a wrong case results + in returning a ENOENT. */ + if (pcheck_case == PCHECK_STRICT) + { + case_clash = TRUE; + return FALSE; + } + + /* PCHECK_ADJUST adjusts the case in the incoming + path which points to the path in *this. */ + strncpy (orig_path + (c - path), data.cFileName, + strlen (data.cFileName)); + } + } + return TRUE; +} + /* readlink system call */ extern "C" @@ -2855,7 +2963,8 @@ chdir (const char *dir) we'll see if Cygwin mailing list users whine about the current behavior. */ if (res == -1) __seterrno (); - else if (!path.has_symlinks () && strpbrk (dir, ":\\") == NULL) + else if (!path.has_symlinks () && strpbrk (dir, ":\\") == NULL + && pcheck_case == PCHECK_RELAXED) cygcwd.set (path, dir); else cygcwd.set (path, NULL); diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h index bdca965b4..4f9df23b0 100644 --- a/winsup/cygwin/path.h +++ b/winsup/cygwin/path.h @@ -25,6 +25,13 @@ enum pathconv_arg PC_NULLEMPTY = 0x0020 }; +enum case_checking +{ + PCHECK_RELAXED = 0, + PCHECK_ADJUST = 1, + PCHECK_STRICT = 2 +}; + #define PC_NONULLEMPTY -1 #include @@ -89,6 +96,8 @@ class path_conv DWORD fileattr; + BOOL case_clash; + void check (const char *src, unsigned opt = PC_SYM_FOLLOW, const suffix_info *suffixes = NULL) __attribute__ ((regparm(3))); @@ -171,3 +180,6 @@ struct cwdstuff }; extern cwdstuff cygcwd; + +extern int pathmatch (const char *path1, const char *path2); +extern int pathnmatch (const char *path1, const char *path2, int len); diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc index f7f51999a..933baafcf 100644 --- a/winsup/cygwin/syscalls.cc +++ b/winsup/cygwin/syscalls.cc @@ -545,7 +545,7 @@ _link (const char *a, const char *b) } if (real_b.error) { - set_errno (real_b.error); + set_errno (real_b.case_clash ? ECASECLASH : real_b.error); goto done; } -- 2.43.5