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