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 8027155
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
31import java.io.File;
32import java.io.FileFilter;
33import java.io.FileInputStream;
34import java.io.FileOutputStream;
35import java.io.RandomAccessFile;
36import java.io.FileNotFoundException;
37import java.io.FilenameFilter;
38import java.io.IOException;
39import java.net.MalformedURLException;
40import java.nio.file.InvalidPathException;
41import java.io.ByteArrayInputStream;
42import java.io.ByteArrayOutputStream;
43import java.io.ObjectOutputStream;
44import java.io.ObjectInputStream;
45
46public 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                String actual = ex.getMessage();
606                String expected = "Prefix string \"" + prefix +
607                    "\" too short: length must be at least 3";
608                if (actual != null && actual.equals(expected))
609                    exceptionThrown = true;
610            } catch (IOException ioe) {
611                System.err.println("IOException happens in testCreateTempFile");
612                System.err.println(ioe.getMessage());
613            }
614        } else {
615            try {
616                File.createTempFile(prefix, suffix, directory);
617            } catch (IOException ex) {
618                String err = "Unable to create temporary file";
619                if (ex.getMessage() != null && ex.getMessage().startsWith(err))
620                    exceptionThrown = true;
621                else {
622                    throw new RuntimeException("Get IOException with message, "
623                            + ex.getMessage() + ", expect message, "+ err);
624                }
625            }
626        }
627        if (!exceptionThrown) {
628            throw new RuntimeException("createTempFile() should throw"
629                    + (shortPrefix ? " IllegalArgumentException"
630                                   : " IOException"));
631        }
632    }
633}
634