1 /*
   2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 /* @test
  25  * @bug 8003992
  26  * @summary Test a file whose path name is embedded with NUL character, and
  27  *          ensure it is handled correctly.
  28  * @author Dan Xu
  29  */
  30 
  31 import java.io.File;
  32 import java.io.FileFilter;
  33 import java.io.FileInputStream;
  34 import java.io.FileOutputStream;
  35 import java.io.RandomAccessFile;
  36 import java.io.FileNotFoundException;
  37 import java.io.FilenameFilter;
  38 import java.io.IOException;
  39 import java.net.MalformedURLException;
  40 import java.nio.file.InvalidPathException;
  41 import java.io.ByteArrayInputStream;
  42 import java.io.ByteArrayOutputStream;
  43 import java.io.ObjectOutputStream;
  44 import java.io.ObjectInputStream;
  45 
  46 public class NulFile {
  47 
  48     private static final char CHAR_NUL = '\u0000';
  49 
  50     private static final String ExceptionMsg = "Invalid file path";
  51 
  52     public static void main(String[] args) {
  53         testFile();
  54         testFileInUnix();
  55         testFileInWindows();
  56         testTempFile();
  57     }
  58 
  59     private static void testFile() {
  60         test(new File(new StringBuilder().append(CHAR_NUL).toString()));
  61         test(new File(
  62                 new StringBuilder().append("").append(CHAR_NUL).toString()));
  63         test(new File(
  64                 new StringBuilder().append(CHAR_NUL).append("").toString()));
  65     }
  66 
  67     private static void testFileInUnix() {
  68         String osName = System.getProperty("os.name");
  69         if (osName.startsWith("Windows"))
  70             return;
  71 
  72         String unixFile = "/";
  73         test(unixFile);
  74 
  75         unixFile = "//";
  76         test(unixFile);
  77 
  78         unixFile = "data/info";
  79         test(unixFile);
  80 
  81         unixFile = "/data/info";
  82         test(unixFile);
  83 
  84         unixFile = "//data//info";
  85         test(unixFile);
  86     }
  87 
  88     private static void testFileInWindows() {
  89         String osName = System.getProperty("os.name");
  90         if (!osName.startsWith("Windows"))
  91             return;
  92 
  93         String windowsFile = "\\";
  94         test(windowsFile);
  95 
  96         windowsFile = "\\\\";
  97         test(windowsFile);
  98 
  99         windowsFile = "/";
 100         test(windowsFile);
 101 
 102         windowsFile = "//";
 103         test(windowsFile);
 104 
 105         windowsFile = "/\\";
 106         test(windowsFile);
 107 
 108         windowsFile = "\\/";
 109         test(windowsFile);
 110 
 111         windowsFile = "data\\info";
 112         test(windowsFile);
 113 
 114         windowsFile = "\\data\\info";
 115         test(windowsFile);
 116 
 117         windowsFile = "\\\\server\\data\\info";
 118         test(windowsFile);
 119 
 120         windowsFile = "z:data\\info";
 121         test(windowsFile);
 122 
 123         windowsFile = "z:\\data\\info";
 124         test(windowsFile);
 125     }
 126 
 127     private static void test(final String name) {
 128         int length = name.length();
 129 
 130         for (int i = 0; i <= length; i++) {
 131             StringBuilder sbName = new StringBuilder(name);
 132             sbName.insert(i, CHAR_NUL);
 133             String curName = sbName.toString();
 134 
 135             // test File(String parent, String child)
 136             File testFile = new File(curName, "child");
 137             test(testFile);
 138             testFile = new File("parent", curName);
 139             test(testFile);
 140 
 141             // test File(String pathname)
 142             testFile = new File(curName);
 143             test(testFile);
 144 
 145             // test File(File parent, String child)
 146             testFile = new File(new File(curName), "child");
 147             test(testFile);
 148             testFile = new File(new File("parent"), curName);
 149             test(testFile);
 150 
 151             // test FileInputStream
 152             testFileInputStream(curName);
 153 
 154             // test FileOutputStream
 155             testFileOutputStream(curName);
 156 
 157             // test RandomAccessFile
 158             testRandomAccessFile(curName);
 159         }
 160     }
 161 
 162     private static void testFileInputStream(final String str) {
 163         boolean exceptionThrown = false;
 164         FileInputStream is = null;
 165         try {
 166             is = new FileInputStream(str);
 167         } catch (FileNotFoundException ex) {
 168             if (ExceptionMsg.equals(ex.getMessage()))
 169                 exceptionThrown = true;
 170         }
 171         if (!exceptionThrown) {
 172             throw new RuntimeException("FileInputStream constructor"
 173                     + " should throw FileNotFoundException");
 174         }
 175         if (is != null) {
 176             throw new RuntimeException("FileInputStream constructor"
 177                     + " should fail");
 178         }
 179 
 180         exceptionThrown = false;
 181         is = null;
 182         try {
 183             is = new FileInputStream(new File(str));
 184         } catch (FileNotFoundException ex) {
 185             if (ExceptionMsg.equals(ex.getMessage()))
 186                 exceptionThrown = true;
 187         }
 188         if (!exceptionThrown) {
 189             throw new RuntimeException("FileInputStream constructor"
 190                     + " should throw FileNotFoundException");
 191         }
 192         if (is != null) {
 193             throw new RuntimeException("FileInputStream constructor"
 194                     + " should fail");
 195         }
 196     }
 197 
 198     private static void testFileOutputStream(final String str) {
 199         boolean exceptionThrown = false;
 200         FileOutputStream os = null;
 201         try {
 202             os = new FileOutputStream(str);
 203         } catch (FileNotFoundException ex) {
 204             if (ExceptionMsg.equals(ex.getMessage()))
 205                 exceptionThrown = true;
 206         }
 207         if (!exceptionThrown) {
 208             throw new RuntimeException("FileOutputStream constructor"
 209                     + " should throw FileNotFoundException");
 210         }
 211         if (os != null) {
 212             throw new RuntimeException("FileOutputStream constructor"
 213                     + " should fail");
 214         }
 215 
 216         exceptionThrown = false;
 217         os = null;
 218         try {
 219             os = new FileOutputStream(new File(str));
 220         } catch (FileNotFoundException ex) {
 221             if (ExceptionMsg.equals(ex.getMessage()))
 222                 exceptionThrown = true;
 223         }
 224         if (!exceptionThrown) {
 225             throw new RuntimeException("FileOutputStream constructor"
 226                     + " should throw FileNotFoundException");
 227         }
 228         if (os != null) {
 229             throw new RuntimeException("FileOutputStream constructor"
 230                     + " should fail");
 231         }
 232     }
 233 
 234     private static void testRandomAccessFile(final String str) {
 235         boolean exceptionThrown = false;
 236         RandomAccessFile raf = null;
 237         String[] modes = {"r", "rw", "rws", "rwd"};
 238 
 239         for (String mode : modes) {
 240             try {
 241                 raf = new RandomAccessFile(str, mode);
 242             } catch (FileNotFoundException ex) {
 243                 if (ExceptionMsg.equals(ex.getMessage()))
 244                     exceptionThrown = true;
 245             }
 246             if (!exceptionThrown) {
 247                 throw new RuntimeException("RandomAccessFile constructor"
 248                         + " should throw FileNotFoundException");
 249             }
 250             if (raf != null) {
 251                 throw new RuntimeException("RandomAccessFile constructor"
 252                         + " should fail");
 253             }
 254 
 255             exceptionThrown = false;
 256             raf = null;
 257             try {
 258                 raf = new RandomAccessFile(new File(str), mode);
 259             } catch (FileNotFoundException ex) {
 260                 if (ExceptionMsg.equals(ex.getMessage()))
 261                     exceptionThrown = true;
 262             }
 263             if (!exceptionThrown) {
 264                 throw new RuntimeException("RandomAccessFile constructor"
 265                         + " should throw FileNotFoundException");
 266             }
 267             if (raf != null) {
 268                 throw new RuntimeException("RandomAccessFile constructor"
 269                         + " should fail");
 270             }
 271         }
 272     }
 273 
 274     private static void test(File testFile) {
 275         test(testFile, false);
 276         // test serialization
 277         testSerialization(testFile);
 278     }
 279 
 280     @SuppressWarnings("deprecation")
 281     private static void test(File testFile, boolean derived) {
 282         boolean exceptionThrown = false;
 283 
 284         if (testFile == null) {
 285             throw new RuntimeException("test file should not be null.");
 286         }
 287 
 288         // getPath()
 289         if (testFile.getPath().indexOf(CHAR_NUL) < 0) {
 290             throw new RuntimeException(
 291                     "File path should contain Nul character");
 292         }
 293         // getAbsolutePath()
 294         if (testFile.getAbsolutePath().indexOf(CHAR_NUL) < 0) {
 295             throw new RuntimeException(
 296                     "File absolute path should contain Nul character");
 297         }
 298         // getAbsoluteFile()
 299         File derivedAbsFile = testFile.getAbsoluteFile();
 300         if (derived) {
 301             if (derivedAbsFile.getPath().indexOf(CHAR_NUL) < 0) {
 302                 throw new RuntimeException(
 303                         "Derived file path should also contain Nul character");
 304             }
 305         } else {
 306             test(derivedAbsFile, true);
 307         }
 308         // getCanonicalPath()
 309         try {
 310             exceptionThrown = false;
 311             testFile.getCanonicalPath();
 312         } catch (IOException ex) {
 313             if (ExceptionMsg.equals(ex.getMessage()))
 314                 exceptionThrown = true;
 315         }
 316         if (!exceptionThrown) {
 317             throw new RuntimeException(
 318                     "getCanonicalPath() should throw IOException with"
 319                         + " message \"" + ExceptionMsg + "\"");
 320         }
 321         // getCanonicalFile()
 322         try {
 323             exceptionThrown = false;
 324             testFile.getCanonicalFile();
 325         } catch (IOException ex) {
 326             if (ExceptionMsg.equals(ex.getMessage()))
 327                 exceptionThrown = true;
 328         }
 329         if (!exceptionThrown) {
 330             throw new RuntimeException(
 331                     "getCanonicalFile() should throw IOException with"
 332                         + " message \"" + ExceptionMsg + "\"");
 333         }
 334         // toURL()
 335         try {
 336             exceptionThrown = false;
 337             testFile.toURL();
 338         } catch (MalformedURLException ex) {
 339             if (ExceptionMsg.equals(ex.getMessage()))
 340                 exceptionThrown = true;
 341         }
 342         if (!exceptionThrown) {
 343             throw new RuntimeException("toURL() should throw IOException with"
 344                 + " message \"" + ExceptionMsg + "\"");
 345         }
 346         // canRead()
 347         if (testFile.canRead())
 348             throw new RuntimeException("File should not be readable");
 349         // canWrite()
 350         if (testFile.canWrite())
 351             throw new RuntimeException("File should not be writable");
 352         // exists()
 353         if (testFile.exists())
 354             throw new RuntimeException("File should not be existed");
 355         // isDirectory()
 356         if (testFile.isDirectory())
 357             throw new RuntimeException("File should not be a directory");
 358         // isFile()
 359         if (testFile.isFile())
 360             throw new RuntimeException("File should not be a file");
 361         // isHidden()
 362         if (testFile.isHidden())
 363             throw new RuntimeException("File should not be hidden");
 364         // lastModified()
 365         if (testFile.lastModified() != 0L)
 366             throw new RuntimeException("File last modified time should be 0L");
 367         // length()
 368         if (testFile.length() != 0L)
 369             throw new RuntimeException("File length should be 0L");
 370         // createNewFile()
 371         try {
 372             exceptionThrown = false;
 373             testFile.createNewFile();
 374         } catch (IOException ex) {
 375             if (ExceptionMsg.equals(ex.getMessage()))
 376                 exceptionThrown = true;
 377         }
 378         if (!exceptionThrown) {
 379             throw new RuntimeException(
 380                     "createNewFile() should throw IOException with"
 381                         + " message \"" + ExceptionMsg + "\"");
 382         }
 383         // delete()
 384         if (testFile.delete())
 385             throw new RuntimeException("Delete operation should fail");
 386         // list()
 387         if (testFile.list() != null)
 388             throw new RuntimeException("File list() should return null");
 389         // list(FilenameFilter)
 390         FilenameFilter fnFilter = new FilenameFilter() {
 391             @Override
 392             public boolean accept(File dir, String name) {
 393                 return false;
 394             }
 395         };
 396         if (testFile.list(fnFilter) != null) {
 397             throw new RuntimeException("File list(FilenameFilter) should"
 398                 + " return null");
 399         }
 400         // listFiles()
 401         if (testFile.listFiles() != null)
 402             throw new RuntimeException("File listFiles() should return null");
 403         // listFiles(FilenameFilter)
 404         if (testFile.listFiles(fnFilter) != null) {
 405             throw new RuntimeException("File listFiles(FilenameFilter)"
 406                 + " should return null");
 407         }
 408         // listFiles(FileFilter)
 409         FileFilter fFilter = new FileFilter() {
 410             @Override
 411             public boolean accept(File file) {
 412                 return false;
 413             }
 414         };
 415         if (testFile.listFiles(fFilter) != null) {
 416             throw new RuntimeException("File listFiles(FileFilter)"
 417                 + " should return null");
 418         }
 419         // mkdir()
 420         if (testFile.mkdir()) {
 421             throw new RuntimeException("File should not be able to"
 422                 + " create directory");
 423         }
 424         // mkdirs()
 425         if (testFile.mkdirs()) {
 426             throw new RuntimeException("File should not be able to"
 427                 + " create directories");
 428         }
 429         // renameTo(File)
 430         if (testFile.renameTo(new File("dest")))
 431             throw new RuntimeException("File rename should fail");
 432         if (new File("dest").renameTo(testFile))
 433             throw new RuntimeException("File rename should fail");
 434         try {
 435             exceptionThrown = false;
 436             testFile.renameTo(null);
 437         } catch (NullPointerException ex) {
 438             exceptionThrown = true;
 439         }
 440         if (!exceptionThrown) {
 441             throw new RuntimeException("File rename should thrown NPE");
 442         }
 443         // setLastModified(long)
 444         if (testFile.setLastModified(0L)) {
 445             throw new RuntimeException("File should fail to set"
 446                 + " last modified time");
 447         }
 448         try {
 449             exceptionThrown = false;
 450             testFile.setLastModified(-1);
 451         } catch (IllegalArgumentException ex) {
 452             if ("Negative time".equals(ex.getMessage()))
 453                 exceptionThrown = true;
 454         }
 455         if (!exceptionThrown) {
 456             throw new RuntimeException("File should fail to set"
 457                 + " last modified time with message \"Negative time\"");
 458         }
 459         // setReadOnly()
 460         if (testFile.setReadOnly())
 461             throw new RuntimeException("File should fail to set read-only");
 462         // setWritable(boolean writable, boolean ownerOnly)
 463         if (testFile.setWritable(true, true))
 464             throw new RuntimeException("File should fail to set writable");
 465         if (testFile.setWritable(true, false))
 466             throw new RuntimeException("File should fail to set writable");
 467         if (testFile.setWritable(false, true))
 468             throw new RuntimeException("File should fail to set writable");
 469         if (testFile.setWritable(false, false))
 470             throw new RuntimeException("File should fail to set writable");
 471         // setWritable(boolean writable)
 472         if (testFile.setWritable(false))
 473             throw new RuntimeException("File should fail to set writable");
 474         if (testFile.setWritable(true))
 475             throw new RuntimeException("File should fail to set writable");
 476         // setReadable(boolean readable, boolean ownerOnly)
 477         if (testFile.setReadable(true, true))
 478             throw new RuntimeException("File should fail to set readable");
 479         if (testFile.setReadable(true, false))
 480             throw new RuntimeException("File should fail to set readable");
 481         if (testFile.setReadable(false, true))
 482             throw new RuntimeException("File should fail to set readable");
 483         if (testFile.setReadable(false, false))
 484             throw new RuntimeException("File should fail to set readable");
 485         // setReadable(boolean readable)
 486         if (testFile.setReadable(false))
 487             throw new RuntimeException("File should fail to set readable");
 488         if (testFile.setReadable(true))
 489             throw new RuntimeException("File should fail to set readable");
 490         // setExecutable(boolean executable, boolean ownerOnly)
 491         if (testFile.setExecutable(true, true))
 492             throw new RuntimeException("File should fail to set executable");
 493         if (testFile.setExecutable(true, false))
 494             throw new RuntimeException("File should fail to set executable");
 495         if (testFile.setExecutable(false, true))
 496             throw new RuntimeException("File should fail to set executable");
 497         if (testFile.setExecutable(false, false))
 498             throw new RuntimeException("File should fail to set executable");
 499         // setExecutable(boolean executable)
 500         if (testFile.setExecutable(false))
 501             throw new RuntimeException("File should fail to set executable");
 502         if (testFile.setExecutable(true))
 503             throw new RuntimeException("File should fail to set executable");
 504         // canExecute()
 505         if (testFile.canExecute())
 506             throw new RuntimeException("File should not be executable");
 507         // getTotalSpace()
 508         if (testFile.getTotalSpace() != 0L)
 509             throw new RuntimeException("The total space should be 0L");
 510         // getFreeSpace()
 511         if (testFile.getFreeSpace() != 0L)
 512             throw new RuntimeException("The free space should be 0L");
 513         // getUsableSpace()
 514         if (testFile.getUsableSpace() != 0L)
 515             throw new RuntimeException("The usable space should be 0L");
 516         // compareTo(File null)
 517         try {
 518             exceptionThrown = false;
 519             testFile.compareTo(null);
 520         } catch (NullPointerException ex) {
 521             exceptionThrown = true;
 522         }
 523         if (!exceptionThrown) {
 524             throw new RuntimeException("compareTo(null) should throw NPE");
 525         }
 526         // toString()
 527         if (testFile.toString().indexOf(CHAR_NUL) < 0) {
 528             throw new RuntimeException(
 529                     "File path should contain Nul character");
 530         }
 531         // toPath()
 532         try {
 533             exceptionThrown = false;
 534             testFile.toPath();
 535         } catch (InvalidPathException ex) {
 536             exceptionThrown = true;
 537         }
 538         if (!exceptionThrown) {
 539             throw new RuntimeException("toPath() should throw"
 540                 + " InvalidPathException");
 541         }
 542     }
 543 
 544     private static void testSerialization(File testFile) {
 545         String path = testFile.getPath();
 546         try {
 547             // serialize test file
 548             ByteArrayOutputStream baos = new ByteArrayOutputStream();
 549             ObjectOutputStream oos = new ObjectOutputStream(baos);
 550             oos.writeObject(testFile);
 551             oos.close();
 552             // deserialize test file
 553             byte[] bytes = baos.toByteArray();
 554             ByteArrayInputStream is = new ByteArrayInputStream(bytes);
 555             ObjectInputStream ois = new ObjectInputStream(is);
 556             File newFile = (File) ois.readObject();
 557             // test
 558             String newPath = newFile.getPath();
 559             if (!path.equals(newPath)) {
 560                 throw new RuntimeException(
 561                         "Serialization should not change file path");
 562             }
 563             test(newFile, false);
 564         } catch (IOException | ClassNotFoundException ex) {
 565             System.err.println("Exception happens in testSerialization");
 566             System.err.println(ex.getMessage());
 567         }
 568     }
 569 
 570     private static void testTempFile() {
 571         final String[] names = {"x", "xx", "xxx", "xxxx"};
 572         final String shortPrefix = "sp";
 573         final String prefix = "prefix";
 574         final String suffix = "suffix";
 575         File tmpDir = new File("tmpDir");
 576 
 577         for (String name : names) {
 578             int length = name.length();
 579             for (int i = 0; i <= length; i++) {
 580                 StringBuilder sbName = new StringBuilder(name);
 581                 sbName.insert(i, CHAR_NUL);
 582                 String curName = sbName.toString();
 583 
 584                 // test prefix
 585                 testCreateTempFile(curName, suffix, tmpDir);
 586                 // test suffix
 587                 testCreateTempFile(shortPrefix, curName, tmpDir);
 588                 testCreateTempFile(prefix, curName, tmpDir);
 589                 // test directory
 590                 testCreateTempFile(shortPrefix, suffix, new File(curName));
 591                 testCreateTempFile(prefix, suffix, new File(curName));
 592             }
 593         }
 594     }
 595 
 596     private static void testCreateTempFile(String prefix, String suffix,
 597                                            File directory) {
 598         // createTempFile(String prefix, String suffix, File directory)
 599         boolean exceptionThrown = false;
 600         boolean shortPrefix = (prefix.length() < 3);
 601         if (shortPrefix) {
 602             try {
 603                 File.createTempFile(prefix, suffix, directory);
 604             } catch (IllegalArgumentException ex) {
 605                 if ("Prefix string too short".equals(ex.getMessage()))
 606                     exceptionThrown = true;
 607             } catch (IOException ioe) {
 608                 System.err.println("IOException happens in testCreateTempFile");
 609                 System.err.println(ioe.getMessage());
 610             }
 611         } else {
 612             try {
 613                 File.createTempFile(prefix, suffix, directory);
 614             } catch (IOException ex) {
 615                 if (ExceptionMsg.equals(ex.getMessage()))
 616                     exceptionThrown = true;
 617             }
 618         }
 619         if (!exceptionThrown) {
 620             throw new RuntimeException("createTempFile() should throw"
 621                     + (shortPrefix ? " IllegalArgumentException"
 622                                    : " IOException"));
 623         }
 624     }
 625 }