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 }