Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-4956115

File.exists() reports false for symbolic links to nonexistent files

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Not an Issue
    • Icon: P4 P4
    • None
    • 1.4.2
    • core-libs
    • x86
    • linux



      Name: gm110360 Date: 11/18/2003


      FULL PRODUCT VERSION :
      java version "1.4.2"
      Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2-b28)
      Java HotSpot(TM) Client VM (build 1.4.2-b28, mixed mode)


      FULL OS VERSION :
      Linux fasthat 2.4.20-19.9 #1 Tue Jul 15 17:03:30 EDT 2003 i686 athlon i386 GNU/Linux


      A DESCRIPTION OF THE PROBLEM :
      File.exists() reports false for symbolic link files that DO exist,
      but refer to nonexistent targets. This is a bug.

      If I have a File object that is a non-canonicalized path pointing
      to the file (that has an inode) which is a symbolic link,
      then it exists() should report true.

      This is because we need to manipulate symbolic links as files in their own right.
      Note that File.delete() will work just fine on such a file (a symlink with
      no existing target). So far I've only noticed that exists() messes up.

      So given that the file /tmp/nonexistentfile does NOT exist,
      and that I create a link as with:

      ln -s /tmp/nonexistentfile /tmp/nonexistentfilesymlink

      The FILE /tmp/nonexistentfilesymlink DOES exist,and exists() should return
      true, but it doesn't and that's the bug.

      STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
      See attached Java program that reproduces the bug. I've only tested it on Linux.

      EXPECTED VERSUS ACTUAL BEHAVIOR :
      EXPECTED -
      The program output should say that the file exists, but says the file doesn't exist.
      ACTUAL -
      Here's the test output on my machine:
      Here's what 'ls -ld' says about the symbolic link BEFORE our attempt to create it:
      /bin/ls: /tmp/nonexistentfilesymlink: No such file or directory

      Here's what 'ls -ld' says about the symbolic link AFTER our attempt to create it:
      lrwxrwxrwx 1 dave dave 20 Sep 13 11:46 /tmp/nonexistentfilesymlink -> /tmp/nonexistentfile


      The bug reproduces on your system.
      /tmp/nonexistentfilesymlink.exists() reports FALSE, despite the fact the link file is there.

      Curiously, File.delete() on the symlink works which is inconsistent with exists() behavior,
      but this IS the desired behavior. (PLEASE DO NOT CHANGE IT!)

      Here's what 'ls -ld' says about the symbolic link as we wind up the test:
      /bin/ls: /tmp/nonexistentfilesymlink: No such file or directory


      REPRODUCIBILITY :
      This bug can be reproduced always.

      ---------- BEGIN SOURCE ----------
      import java.io.* ;
      import java.util.* ;

      /**
       * File.exists() fails for symbolic links referring to nonexistent targets.
       * I believe that a file specification identifying the LINK, and not the target of the link,
       * should return true if the link file exists (after all, it has an inode, etc).
       * I can always query the existence of the link target by calling exists on the
       * canonical form of the file.
       *
       * This bug was tested against JDK 1.4.2 (build 1.4.2-b28) on RedHat Linux 9.0
       * Linux version 2.4.20-19.9 (###@###.###) (gcc version 3.2.2 20030222 (Red Hat Linux 3.2.2-5)) #1 Tue Jul 15 17:03:30 EDT 2003
       *
       * @author Dave Tenny
       */

      public class Bug2
      {
        static String linkTargetName = "/tmp/nonexistentfile" ; // will not exist
        static String symlinkFileName = "/tmp/nonexistentfilesymlink" ; // will exist
        static File linkTargetFile = new File(linkTargetName) ; // will not exist
        static File symLinkFile = new File(symlinkFileName) ; // will exist

        public static void main(String[] args) {
          if (linkTargetFile.exists()) {
            System.out.println("This test will not function properly if the file " + linkTargetName +
      " exists. Please delete it and run the test again.") ;
            return ;
          }

          System.out.println("Here's what 'ls -ld' says about the symbolic link " +
      "BEFORE our attempt to create it:") ;
          lsLink() ;

          // Can't actually check for existence since that's the whole bug we're reporting.
          // I.e. symLinkFile.exists() will always return false!
          if (!makelink(linkTargetFile, symLinkFile))
            // Note it and continue
            System.out.println("Unable to create symbolic link file "
      + symLinkFile
      + ", perhaps it already existed from a prior run.") ;

          System.out.println("Here's what 'ls -ld' says about the symbolic link " +
      "AFTER our attempt to create it:") ;
          lsLink() ;

          System.out.println() ;
          if (symLinkFile.exists())
            System.out.println("The bug does NOT reproduce on your system.") ;
          else
            System.out.println("The bug reproduces on your system.\n"
      + symLinkFile +
      ".exists() reports FALSE, despite the fact the link file is there.") ;
          System.out.println() ;

          if (symLinkFile.delete())
            System.out.println("Curiously, File.delete() on the symlink works " +
      "which is inconsistent with exists() behavior,\n" +
      "but this IS the desired behavior. (PLEASE DO NOT CHANGE IT!)") ;
          else
            System.out.println("symLinkFile.delete() fails, which is consistent exists(), "
      + "but is undesirable since we can't delete the link file.") ;

          System.out.println() ;
          System.out.println("Here's what 'ls -ld' says about the symbolic link as we wind up the test:") ;
          lsLink() ;
        } // main()

        // Yes, I know, need stream gobblers for process output. It works for our bug-reporting purposes here.
        private static boolean doSimpleCommand(String[] args) {
          Runtime rt = Runtime.getRuntime() ;
          try {
            Process proc = rt.exec(args) ;
            int result = proc.waitFor() ;
            StringBuffer buffer = new StringBuffer(128) ;

            // stdout
            try {
              InputStream stream = proc.getInputStream() ;
              InputStreamReader reader = new InputStreamReader(stream) ;
              int ch ;
              while ((ch = reader.read()) != -1)
                buffer.append((char)ch) ;
            } catch (IOException e) {
      buffer.append(e.toString()) ;
            }
            if (buffer.length() > 0)
      System.out.println(buffer.toString()) ;
            buffer.setLength(0) ; // reset for next use

            // stderr
            try {
              InputStream stream = proc.getErrorStream() ;
              InputStreamReader reader = new InputStreamReader(stream) ;
              int ch ;
              while ((ch = reader.read()) != -1)
                buffer.append((char)ch) ;
            } catch (IOException e) {
      buffer.append(e.toString()) ;
            }
            if (buffer.length() > 0)
      System.out.println(buffer.toString()) ;

            // Done
            return result == 0 ;
          } catch (Exception e) {
            System.out.println(args[0] + " exception: " + e.toString()) ;
            return false ;
          }
        } // doSimpleCommand()

        private static boolean makelink(File target, File source) {
          // "ln -s /tmp/dir /tmp/symdir"
          String[] args = {"/bin/ln", "-s", target.toString(), source.toString()} ;
          return doSimpleCommand(args) ;
        }

        private static boolean lsLink() {
          String[] args = {"/bin/ls", "-ld", symlinkFileName} ;
          return doSimpleCommand(args) ;
        }

      } // class Bug2

      ---------- END SOURCE ----------

      CUSTOMER SUBMITTED WORKAROUND :
      I haven't implemented one yet. I suspect the workaround is to
      check that getCanonicalPath() doesn't equal getAbsolutePath(),
      and if so we know the file is a link.

      However it isn't that simple,
      we must first canonicalize all elements of the parent path of the
      link file (such as higher-up symbolic links, "." and ".." references, etc), and I just reported a bug yesterday that catches File.getCanonicalPath() red handed lying about symbolic link files (whose targets DO exist), but is VERY difficult to reproduce.

      This bug is hard to work around and should be fixed.
      The JDK currently makes it very difficult to write applications that
      manages files on a unix machine.

      Another possible work around is to shell out with exec, but that adds
      a lot of overhead to the call and isn't practical when dealing with thousands
      or tens of thousands of files. Exec has also been very bug prone and makes
      it harder to ship to customers because they have to have just the right java version.
      (Incident Review ID: 207260)
      ======================================================================

            mr Mark Reinhold
            gmanwanisunw Girish Manwani (Inactive)
            Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

              Created:
              Updated:
              Resolved:
              Imported:
              Indexed: