1 /* 2 * Copyright (c) 2005, 2020, 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 * @test 26 * @bug 4057701 6286712 6364377 27 * @requires (os.family == "linux" | os.family == "mac" | os.family == "windows") 28 * @run build GetXSpace 29 * @run shell GetXSpace.sh 30 * @summary Basic functionality of File.get-X-Space methods. 31 * @key randomness 32 */ 33 34 import java.io.BufferedReader; 35 import java.io.File; 36 import java.io.FilePermission; 37 import java.io.InputStreamReader; 38 import java.io.IOException; 39 import java.nio.file.Files; 40 import java.nio.file.FileStore; 41 import java.security.Permission; 42 import java.util.ArrayList; 43 import java.util.regex.Matcher; 44 import java.util.regex.Pattern; 45 46 import static java.lang.System.out; 47 48 public class GetXSpace { 49 50 private static SecurityManager [] sma = { null, new Allow(), new DenyFSA(), 51 new DenyRead() }; 52 53 private static final String OS_NAME = System.getProperty("os.name"); 54 private static final boolean IS_MAC = OS_NAME.startsWith("Mac"); 55 private static final boolean IS_WIN = OS_NAME.startsWith("Windows"); 56 57 // FileSystem Total Used Available Use% MountedOn 58 private static final Pattern DF_PATTERN = Pattern.compile("([^\\s]+)\\s+(\\d+)\\s+\\d+\\s+(\\d+)\\s+\\d+%\\s+([^\\s].*)\n"); 59 60 private static int fail = 0; 61 private static int pass = 0; 62 private static Throwable first; 63 64 static void pass() { 65 pass++; 66 } 67 68 static void fail(String p) { 69 setFirst(p); 70 System.err.format("FAILED: %s%n", p); 71 fail++; 72 } 73 74 static void fail(String p, long exp, String cmp, long got) { 75 String s = String.format("'%s': %d %s %d", p, exp, cmp, got); 76 setFirst(s); 77 System.err.format("FAILED: %s%n", s); 78 fail++; 79 } 80 81 private static void fail(String p, Class ex) { 82 String s = String.format("'%s': expected %s - FAILED%n", p, ex.getName()); 83 setFirst(s); 84 System.err.format("FAILED: %s%n", s); 85 fail++; 86 } 87 88 private static void setFirst(String s) { 89 if (first == null) { 90 first = new RuntimeException(s); 91 } 92 } 93 94 private static class Space { 95 private static final long KSIZE = 1024; 96 private final String name; 97 private final long total; 98 private final long free; 99 100 Space(String total, String free, String name) { 101 try { 102 this.total = Long.valueOf(total) * KSIZE; 103 this.free = Long.valueOf(free) * KSIZE; 104 } catch (NumberFormatException x) { 105 throw new RuntimeException("the regex should have caught this", x); 106 } 107 this.name = name; 108 } 109 110 String name() { return name; } 111 long total() { return total; } 112 long free() { return free; } 113 boolean woomFree(long freeSpace) { 114 return ((freeSpace >= (free / 10)) && (freeSpace <= (free * 10))); 115 } 116 public String toString() { 117 return String.format("%s (%d/%d)", name, free, total); 118 } 119 } 120 121 private static ArrayList<Space> space(String f) throws IOException { 122 ArrayList<Space> al = new ArrayList<>(); 123 124 String cmd = "df -k -P" + (f == null ? "" : " " + f); 125 StringBuilder sb = new StringBuilder(); 126 Process p = Runtime.getRuntime().exec(cmd); 127 try (BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()))) { 128 String s; 129 int i = 0; 130 while ((s = in.readLine()) != null) { 131 // skip header 132 if (i++ == 0) continue; 133 sb.append(s).append("\n"); 134 } 135 } 136 out.println(sb); 137 138 Matcher m = DF_PATTERN.matcher(sb); 139 int j = 0; 140 while (j < sb.length()) { 141 if (m.find(j)) { 142 // swap can change while this test is running 143 if (!m.group(1).equals("swap")) { 144 String name = f; 145 if (name == null) { 146 // cygwin's df lists windows path as FileSystem (1st group) 147 name = IS_WIN ? m.group(1) : m.group(4); 148 } 149 al.add(new Space(m.group(2), m.group(3), name));; 150 } 151 j = m.end() + 1; 152 } else { 153 throw new RuntimeException("unrecognized df output format: " 154 + "charAt(" + j + ") = '" 155 + sb.charAt(j) + "'"); 156 } 157 } 158 159 if (al.size() == 0) { 160 // df did not produce output 161 String name = (f == null ? "" : f); 162 al.add(new Space("0", "0", name)); 163 } 164 return al; 165 } 166 167 private static void tryCatch(Space s) { 168 out.format("%s:%n", s.name()); 169 File f = new File(s.name()); 170 SecurityManager sm = System.getSecurityManager(); 171 if (sm instanceof Deny) { 172 String fmt = " %14s: \"%s\" thrown as expected%n"; 173 try { 174 f.getTotalSpace(); 175 fail(s.name(), SecurityException.class); 176 } catch (SecurityException x) { 177 out.format(fmt, "getTotalSpace", x); 178 pass(); 179 } 180 try { 181 f.getFreeSpace(); 182 fail(s.name(), SecurityException.class); 183 } catch (SecurityException x) { 184 out.format(fmt, "getFreeSpace", x); 185 pass(); 186 } 187 try { 188 f.getUsableSpace(); 189 fail(s.name(), SecurityException.class); 190 } catch (SecurityException x) { 191 out.format(fmt, "getUsableSpace", x); 192 pass(); 193 } 194 } 195 } 196 197 private static void compare(Space s) { 198 File f = new File(s.name()); 199 long ts = f.getTotalSpace(); 200 long fs = f.getFreeSpace(); 201 long us = f.getUsableSpace(); 202 203 out.format("%s:%n", s.name()); 204 String fmt = " %-4s total= %12d free = %12d usable = %12d%n"; 205 out.format(fmt, "df", s.total(), 0, s.free()); 206 out.format(fmt, "getX", ts, fs, us); 207 208 // if the file system can dynamically change size, this check will fail 209 if (ts != s.total()) { 210 long blockSize = 1; 211 long numBlocks = 0; 212 try { 213 FileStore fileStore = Files.getFileStore(f.toPath()); 214 blockSize = fileStore.getBlockSize(); 215 numBlocks = fileStore.getTotalSpace()/blockSize; 216 } catch (IOException e) { 217 throw new RuntimeException(e); 218 } 219 220 221 // On macOS, the number of 1024 byte blocks might be incorrectly 222 // calculated by 'df' using integer division by 2 of the number of 223 // 512 byte blocks, resulting in a size smaller than the actual 224 // value when the number of blocks is odd. 225 if (!IS_MAC || blockSize != 512 || numBlocks % 2 == 0 226 || ts - s.total() != 512) { 227 fail(s.name(), s.total(), "!=", ts); 228 } 229 } else { 230 pass(); 231 } 232 233 // unix df returns statvfs.f_bavail 234 long tsp = (!IS_WIN ? us : fs); 235 if (!s.woomFree(tsp)) { 236 fail(s.name(), s.free(), "??", tsp); 237 } else { 238 pass(); 239 } 240 241 if (fs > s.total()) { 242 fail(s.name(), s.total(), ">", fs); 243 } else { 244 pass(); 245 } 246 247 if (us > s.total()) { 248 fail(s.name(), s.total(), ">", us); 249 } else { 250 pass(); 251 } 252 } 253 254 private static String FILE_PREFIX = "/getSpace."; 255 private static void compareZeroNonExist() { 256 File f; 257 while (true) { 258 f = new File(FILE_PREFIX + Math.random()); 259 if (f.exists()) { 260 continue; 261 } 262 break; 263 } 264 265 long [] s = { f.getTotalSpace(), f.getFreeSpace(), f.getUsableSpace() }; 266 267 for (int i = 0; i < s.length; i++) { 268 if (s[i] != 0L) { 269 fail(f.getName(), s[i], "!=", 0L); 270 } else { 271 pass(); 272 } 273 } 274 } 275 276 private static void compareZeroExist() { 277 try { 278 File f = File.createTempFile("tmp", null, new File(".")); 279 280 long [] s = { f.getTotalSpace(), f.getFreeSpace(), f.getUsableSpace() }; 281 282 for (int i = 0; i < s.length; i++) { 283 if (s[i] == 0L) { 284 fail(f.getName(), s[i], "==", 0L); 285 } else { 286 pass(); 287 } 288 } 289 } catch (IOException x) { 290 x.printStackTrace(); 291 fail("Couldn't create temp file for test"); 292 } 293 } 294 295 private static class Allow extends SecurityManager { 296 public void checkRead(String file) {} 297 public void checkPermission(Permission p) {} 298 public void checkPermission(Permission p, Object context) {} 299 } 300 301 private static class Deny extends SecurityManager { 302 public void checkPermission(Permission p) { 303 if (p.implies(new RuntimePermission("setSecurityManager")) 304 || p.implies(new RuntimePermission("getProtectionDomain"))) 305 return; 306 super.checkPermission(p); 307 } 308 309 public void checkPermission(Permission p, Object context) { 310 if (p.implies(new RuntimePermission("setSecurityManager")) 311 || p.implies(new RuntimePermission("getProtectionDomain"))) 312 return; 313 super.checkPermission(p, context); 314 } 315 } 316 317 private static class DenyFSA extends Deny { 318 private String err = "sorry - getFileSystemAttributes"; 319 320 public void checkPermission(Permission p) { 321 if (p.implies(new RuntimePermission("getFileSystemAttributes"))) 322 throw new SecurityException(err); 323 super.checkPermission(p); 324 } 325 326 public void checkPermission(Permission p, Object context) { 327 if (p.implies(new RuntimePermission("getFileSystemAttributes"))) 328 throw new SecurityException(err); 329 super.checkPermission(p, context); 330 } 331 } 332 333 private static class DenyRead extends Deny { 334 private String err = "sorry - checkRead()"; 335 336 public void checkRead(String file) { 337 throw new SecurityException(err); 338 } 339 } 340 341 private static void testFile(String dirName) { 342 out.format("--- Testing %s%n", dirName); 343 ArrayList<Space> l; 344 try { 345 l = space(dirName); 346 } catch (IOException x) { 347 throw new RuntimeException(dirName + " can't get file system information", x); 348 } 349 compare(l.get(0)); 350 } 351 352 private static void testDF() { 353 out.println("--- Testing df"); 354 // Find all of the partitions on the machine and verify that the size 355 // returned by "df" is equivalent to File.getXSpace() values. 356 ArrayList<Space> l; 357 try { 358 l = space(null); 359 } catch (IOException x) { 360 throw new RuntimeException("can't get file system information", x); 361 } 362 if (l.size() == 0) 363 throw new RuntimeException("no partitions?"); 364 365 for (int i = 0; i < sma.length; i++) { 366 System.setSecurityManager(sma[i]); 367 SecurityManager sm = System.getSecurityManager(); 368 if (sma[i] != null && sm == null) 369 throw new RuntimeException("Test configuration error " 370 + " - can't set security manager"); 371 372 out.format("%nSecurityManager = %s%n" , 373 (sm == null ? "null" : sm.getClass().getName())); 374 for (var s : l) { 375 if (sm instanceof Deny) { 376 tryCatch(s); 377 } else { 378 compare(s); 379 compareZeroNonExist(); 380 compareZeroExist(); 381 } 382 } 383 } 384 } 385 386 public static void main(String [] args) { 387 if (args.length > 0) { 388 testFile(args[0]); 389 } else { 390 testDF(); 391 } 392 393 if (fail != 0) { 394 throw new RuntimeException((fail + pass) + " tests: " 395 + fail + " failure(s), first", first); 396 } else { 397 out.format("all %d tests passed%n", fail + pass); 398 } 399 } 400 }