1 /* 2 * Copyright (c) 2016, 2018, 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 25 package org.graalvm.compiler.debug; 26 27 import static java.nio.file.LinkOption.NOFOLLOW_LINKS; 28 29 import java.io.File; 30 import java.io.IOException; 31 import java.nio.file.AccessDeniedException; 32 import java.nio.file.FileAlreadyExistsException; 33 import java.nio.file.Files; 34 import java.nio.file.InvalidPathException; 35 import java.nio.file.Path; 36 import java.nio.file.Paths; 37 38 import org.graalvm.compiler.options.OptionKey; 39 import org.graalvm.compiler.options.OptionValues; 40 41 /** 42 * Miscellaneous methods for modifying and generating file system paths. 43 */ 44 public class PathUtilities { 45 46 /** 47 * Gets a value based on {@code name} that can be passed to {@link Paths#get(String, String...)} 48 * without causing an {@link InvalidPathException}. 49 * 50 * @return {@code name} with all characters invalid for the current file system replaced by 51 * {@code '_'} 52 */ 53 public static String sanitizeFileName(String name) { 54 try { 55 Path path = Paths.get(name); 56 if (path.getNameCount() == 0) { 57 return name; 58 } 59 } catch (InvalidPathException e) { 60 // fall through 61 } 62 StringBuilder buf = new StringBuilder(name.length()); 63 for (int i = 0; i < name.length(); i++) { 64 char c = name.charAt(i); 65 if (c != File.separatorChar && c != ' ' && !Character.isISOControl(c)) { 66 try { 67 Paths.get(String.valueOf(c)); 68 buf.append(c); 69 continue; 70 } catch (InvalidPathException e) { 71 } 72 } 73 buf.append('_'); 74 } 75 return buf.toString(); 76 } 77 78 /** 79 * A maximum file name length supported by most file systems. There is no platform independent 80 * way to get this in Java. Normally it is 255. But for AUFS it is 242. Refer AUFS_MAX_NAMELEN 81 * in http://aufs.sourceforge.net/aufs3/man.html. 82 */ 83 private static final int MAX_FILE_NAME_LENGTH = 242; 84 85 private static final String ELLIPSIS = "..."; 86 87 static Path createUnique(OptionValues options, OptionKey<String> baseNameOption, String id, String label, String ext, boolean createDirectory) throws IOException { 88 String uniqueTag = ""; 89 int dumpCounter = 1; 90 String prefix; 91 if (id == null) { 92 prefix = baseNameOption.getValue(options); 93 int slash = prefix.lastIndexOf(File.separatorChar); 94 prefix = prefix.substring(slash + 1); 95 } else { 96 prefix = id; 97 } 98 for (;;) { 99 int fileNameLengthWithoutLabel = uniqueTag.length() + ext.length() + prefix.length() + "[]".length(); 100 int labelLengthLimit = MAX_FILE_NAME_LENGTH - fileNameLengthWithoutLabel; 101 String fileName; 102 if (labelLengthLimit < ELLIPSIS.length()) { 103 // This means `id` is very long 104 String suffix = uniqueTag + ext; 105 int idLengthLimit = Math.min(MAX_FILE_NAME_LENGTH - suffix.length(), prefix.length()); 106 fileName = sanitizeFileName(prefix.substring(0, idLengthLimit) + suffix); 107 } else { 108 if (label == null) { 109 fileName = sanitizeFileName(prefix + uniqueTag + ext); 110 } else { 111 String adjustedLabel = label; 112 if (label.length() > labelLengthLimit) { 113 adjustedLabel = label.substring(0, labelLengthLimit - ELLIPSIS.length()) + ELLIPSIS; 114 } 115 fileName = sanitizeFileName(prefix + '[' + adjustedLabel + ']' + uniqueTag + ext); 116 } 117 } 118 Path dumpDir = DebugOptions.getDumpDirectory(options); 119 Path result = Paths.get(dumpDir.toString(), fileName); 120 try { 121 if (createDirectory) { 122 return Files.createDirectory(result); 123 } else { 124 try { 125 return Files.createFile(result); 126 } catch (AccessDeniedException e) { 127 /* 128 * Thrown on Windows if a directory with the same name already exists, so 129 * convert it to FileAlreadyExistsException if that's the case. 130 */ 131 throw Files.isDirectory(result, NOFOLLOW_LINKS) ? new FileAlreadyExistsException(e.getFile()) : e; 132 } 133 } 134 } catch (FileAlreadyExistsException e) { 135 uniqueTag = "_" + dumpCounter++; 136 } 137 } 138 } 139 140 }