Index: libjava/java/io/natFilePosix.cc =================================================================== --- libjava/java/io/natFilePosix.cc (revision 112529) +++ libjava/java/io/natFilePosix.cc (working copy) @@ -104,91 +104,121 @@ jstring java::io::File::getCanonicalPath (void) { - // We use `+2' here because we might need to use `.' for our special - // case. - char *buf = (char *) __builtin_alloca (JvGetStringUTFLength (path) + 2); - char buf2[MAXPATHLEN]; - jsize total = JvGetStringUTFRegion (path, 0, path->length(), buf); + jstring path = getAbsolutePath (); - // Special case: treat "" the same as ".". - if (total == 0) - buf[total++] = '.'; + int len = JvGetStringUTFLength (path); + char *src = (char *) _Jv_Malloc (len + 1); + JvGetStringUTFRegion (path, 0, path->length(), src); + src[len] = '\0'; + int srci = 1; - buf[total] = '\0'; + char *dst = (char *) _Jv_Malloc (2); + dst[0] = '/'; + int dsti = 1; -#ifdef HAVE_REALPATH - if (realpath (buf, buf2) == NULL) + bool fschecks = true; + + while (src[srci] != '\0') { - // If realpath failed, we have to come up with a canonical path - // anyway. We do this with purely textual manipulation. - // FIXME: this isn't perfect. You can construct a case where - // we get a different answer from the JDK: - // mkdir -p /tmp/a/b/c - // ln -s /tmp/a/b /tmp/a/z - // ... getCanonicalPath("/tmp/a/z/c/nosuchfile") - // We will give /tmp/a/z/c/nosuchfile, while the JDK will - // give /tmp/a/b/c/nosuchfile. - int out_idx; - if (buf[0] != '/') + // Skip slashes. + while (src[srci] == '/') + srci++; + int tmpi = srci; + // Find next slash. + while (src[srci] != '/' && src[srci] != '\0') + srci++; + if (srci == tmpi) + // We hit the end. + break; + len = srci - tmpi; + + // Handle "." and "..". + if (len == 1 && src[tmpi] == '.') + continue; + if (len == 2 && src[tmpi] == '.' && src[tmpi + 1] == '.') { - // Not absolute, so start with current directory. - if (getcwd (buf2, sizeof (buf2)) == NULL) - throw new IOException (); - out_idx = strlen (buf2); + while (dsti > 1 && dst[dsti - 1] != '/') + dsti--; + if (dsti != 1) + dsti--; + dst = (char *) _Jv_Realloc (dst, dsti + 1); + // Reenable filesystem checking if disabled, as we might + // have reversed over whatever caused the problem before. + // At least one proprietary JVM has inconsistencies because + // it does not do this. + fschecks = true; + continue; } - else + + // Handle real path components. + dst = (char *) _Jv_Realloc (dst, dsti + (dsti > 1 ? 1 : 0) + len + 1); + int dsti_save = dsti; + if (dsti > 1) + dst[dsti++] = '/'; + strncpy (&dst[dsti], &src[tmpi], len); + dsti += len; + if (fschecks == false) + continue; + +#if defined (HAVE_LSTAT) && defined (HAVE_READLINK) + struct stat sb; + dst[dsti] = '\0'; + if (::lstat (dst, &sb) == 0) { - buf2[0] = '/'; - out_idx = 1; - } - int in_idx = 0; - while (buf[in_idx] != '\0') - { - // Skip '/'s. - while (buf[in_idx] == '/') - ++in_idx; - int elt_start = in_idx; - // Find next '/' or end of path. - while (buf[in_idx] != '\0' && buf[in_idx] != '/') - ++in_idx; - if (in_idx == elt_start) + if (S_ISLNK (sb.st_mode)) { - // An empty component means we've reached the end. - break; + int step = 4096; + int size = step; + char *tmp = (char *) _Jv_Malloc (size); + + while (1) + { + tmpi = ::readlink (dst, tmp, size); + if (tmpi < 1) + { + _Jv_Free (src); + _Jv_Free (dst); + _Jv_Free (tmp); + throw new IOException ( + JvNewStringLatin1 ("readlink failed")); + } + if (tmpi < size) + break; + size += step; + tmp = (char *) _Jv_Realloc (tmp, size); + } + + // Prepend the link's path to src. + tmp = (char *) _Jv_Realloc (tmp, tmpi + strlen (&src[srci]) + 1); + strcpy(&tmp[tmpi], &src[srci]); + _Jv_Free (src); + src = tmp; + srci = 0; + + // Either replace or append dst depending on whether the + // link is relative or absolute. + dsti = src[0] == '/' ? 1 : dsti_save; + dst = (char *) _Jv_Realloc (dst, dsti + 1); } - int len = in_idx - elt_start; - if (len == 1 && buf[in_idx] == '.') - continue; - if (len == 2 && buf[in_idx] == '.' && buf[in_idx + 1] == '.') - { - // Found ".." component, lop off last part from existing - // buffer. - --out_idx; - while (out_idx > 0 && buf2[out_idx] != '/') - --out_idx; - // Can't go up past "/". - if (out_idx == 0) - ++out_idx; - } - else - { - // Append a real path component to the output. - if (out_idx > 1) - buf2[out_idx++] = '/'; - strncpy (&buf2[out_idx], &buf[elt_start], len); - out_idx += len; - } } - - buf2[out_idx] = '\0'; + else + { + // Something doesn't exist, or we don't have permission to + // read it, or a previous path component is a directory, or + // a symlink is looped. Whatever, we can't check the + // filesystem any more. + fschecks = false; + } +#endif // HAVE_LSTAT && HAVE_READLINK } + dst[dsti] = '\0'; // FIXME: what encoding to assume for file names? This affects many // calls. - return JvNewStringUTF (buf2); -#else - return JvNewStringUTF (buf); -#endif + path = JvNewStringUTF (dst); + _Jv_Free (src); + _Jv_Free (dst); + return path; } jboolean Index: libjava/java/io/File.java =================================================================== --- libjava/java/io/File.java (revision 112529) +++ libjava/java/io/File.java (working copy) @@ -508,9 +508,9 @@ /** * This method returns a canonical representation of the pathname of * this file. The actual form of the canonical representation is - * different. On the GNU system, the canonical form differs from the - * absolute form in that all relative file references to "." and ".." - * are resolved and removed. + * system-dependent. On the GNU system, conversion to canonical + * form involves the removal of redundant separators, references to + * "." and "..", and symbolic links. *

* Note that this method, unlike the other methods which return path * names, can throw an IOException. This is because native method Index: libjava/configure.ac =================================================================== --- libjava/configure.ac (revision 112529) +++ libjava/configure.ac (working copy) @@ -895,7 +895,7 @@ else AC_CHECK_FUNCS([strerror ioctl select fstat open fsync sleep opendir \ gmtime_r localtime_r readdir_r getpwuid_r getcwd \ - access stat mkdir rename rmdir unlink realpath utime chmod \ + access stat lstat mkdir rename rmdir unlink utime chmod readlink \ nl_langinfo setlocale \ inet_pton uname inet_ntoa \ fork execvp pipe sigaction ftruncate]) Index: libjava/configure =================================================================== --- libjava/configure (revision 112529) +++ libjava/configure (working copy) @@ -9388,9 +9388,10 @@ + for ac_func in strerror ioctl select fstat open fsync sleep opendir \ gmtime_r localtime_r readdir_r getpwuid_r getcwd \ - access stat mkdir rename rmdir unlink realpath utime chmod \ + access stat lstat mkdir rename rmdir unlink utime chmod readlink \ nl_langinfo setlocale \ inet_pton uname inet_ntoa \ fork execvp pipe sigaction ftruncate Index: libjava/include/config.h.in =================================================================== --- libjava/include/config.h.in (revision 112529) +++ libjava/include/config.h.in (working copy) @@ -172,6 +172,9 @@ /* Define to 1 if you have the `localtime_r' function. */ #undef HAVE_LOCALTIME_R +/* Define to 1 if you have the `lstat' function. */ +#undef HAVE_LSTAT + /* Define to 1 if you have the `memcpy' function. */ #undef HAVE_MEMCPY @@ -229,8 +232,8 @@ /* Define to 1 if you have the `readdir_r' function. */ #undef HAVE_READDIR_R -/* Define to 1 if you have the `realpath' function. */ -#undef HAVE_REALPATH +/* Define to 1 if you have the `readlink' function. */ +#undef HAVE_READLINK /* Define to 1 if you have the `rename' function. */ #undef HAVE_RENAME