Skip to content

Commit 3ef834f

Browse files
author
Brian Burkhalter
committed
8298619: java/io/File/GetXSpace.java is failing
Reviewed-by: rriggs
1 parent c594119 commit 3ef834f

File tree

3 files changed

+255
-104
lines changed

3 files changed

+255
-104
lines changed

make/test/JtregNativeJdk.gmk

+2
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,12 @@ ifeq ($(call isTargetOs, windows), true)
7979

8080
BUILD_JDK_JTREG_LIBRARIES_LIBS_libTracePinnedThreads := jvm.lib
8181
BUILD_JDK_JTREG_LIBRARIES_LIBS_libNewDirectByteBuffer := $(WIN_LIB_JAVA)
82+
BUILD_JDK_JTREG_LIBRARIES_LIBS_libGetXSpace := $(WIN_LIB_JAVA)
8283
else
8384
BUILD_JDK_JTREG_LIBRARIES_LIBS_libstringPlatformChars := -ljava
8485
BUILD_JDK_JTREG_LIBRARIES_LIBS_libDirectIO := -ljava
8586
BUILD_JDK_JTREG_LIBRARIES_LIBS_libNewDirectByteBuffer := -ljava
87+
BUILD_JDK_JTREG_LIBRARIES_LIBS_libGetXSpace := -ljava
8688
BUILD_JDK_JTREG_LIBRARIES_LDFLAGS_libNativeThread := -pthread
8789

8890
# java.lang.foreign tests

test/jdk/java/io/File/GetXSpace.java

+107-104
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2005, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2005, 2023, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -29,7 +29,7 @@
2929
* @summary Basic functionality of File.get-X-Space methods.
3030
* @library .. /test/lib
3131
* @build jdk.test.lib.Platform
32-
* @run main/othervm -Djava.security.manager=allow GetXSpace
32+
* @run main/othervm/native -Djava.security.manager=allow GetXSpace
3333
*/
3434

3535
import java.io.BufferedReader;
@@ -52,13 +52,13 @@
5252

5353
@SuppressWarnings("removal")
5454
public class GetXSpace {
55+
static {
56+
System.loadLibrary("GetXSpace");
57+
}
5558

5659
private static SecurityManager [] sma = { null, new Allow(), new DenyFSA(),
5760
new DenyRead() };
5861

59-
// FileSystem Total Used Available Use% MountedOn
60-
private static final Pattern DF_PATTERN = Pattern.compile("([^\\s]+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+\\d+%\\s+([^\\s].*)\n");
61-
6262
private static int fail = 0;
6363
private static int pass = 0;
6464
private static Throwable first;
@@ -100,34 +100,36 @@ private static void setFirst(String s) {
100100
}
101101

102102
private static class Space {
103-
private static final long KSIZE = 1024;
104103
private final String name;
104+
private final long size;
105105
private final long total;
106-
private final long used;
106+
private final long free;
107107
private final long available;
108108

109-
Space(String name, String total, String used, String available) {
109+
Space(String name) {
110110
this.name = name;
111-
try {
112-
this.total = Long.parseLong(total) * KSIZE;
113-
this.used = Long.parseLong(used) * KSIZE;
114-
this.available = Long.parseLong(available) * KSIZE;
115-
} catch (NumberFormatException x) {
116-
throw new RuntimeException("the regex should have caught this", x);
117-
}
111+
long[] sizes = new long[4];
112+
if (getSpace0(name, sizes))
113+
System.err.println("WARNING: total space is estimated");
114+
this.size = sizes[0];
115+
this.total = sizes[1];
116+
this.free = sizes[2];
117+
this.available = sizes[3];
118118
}
119119

120120
String name() { return name; }
121+
long size() { return size; }
121122
long total() { return total; }
122-
long used() { return used; }
123123
long available() { return available; }
124-
long free() { return total - used; }
124+
long free() { return free; }
125+
125126
boolean woomFree(long freeSpace) {
126127
return ((freeSpace >= (available / 10)) &&
127128
(freeSpace <= (available * 10)));
128129
}
130+
129131
public String toString() {
130-
return String.format("%s (%d/%d/%d)", name, total, used, available);
132+
return String.format("%s (%d/%d/%d)", name, total, free, available);
131133
}
132134
}
133135

@@ -149,49 +151,16 @@ private static void diskFree() throws IOException {
149151
out.println(sb);
150152
}
151153

152-
private static ArrayList<Space> space(String f) throws IOException {
153-
ArrayList<Space> al = new ArrayList<>();
154+
private static ArrayList<String> paths() throws IOException {
155+
ArrayList<String> al = new ArrayList<>();
154156

155-
String cmd = "df -k -P" + (f == null ? "" : " " + f);
156-
StringBuilder sb = new StringBuilder();
157-
Process p = Runtime.getRuntime().exec(cmd);
158-
try (BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()))) {
159-
String s;
160-
int i = 0;
161-
while ((s = in.readLine()) != null) {
162-
// skip header
163-
if (i++ == 0) continue;
164-
sb.append(s).append("\n");
165-
}
157+
File[] roots = File.listRoots();
158+
long[] space = new long[4];
159+
for (File root : roots) {
160+
String path = root.toString();
161+
al.add(path);
166162
}
167-
out.println(sb);
168163

169-
Matcher m = DF_PATTERN.matcher(sb);
170-
int j = 0;
171-
while (j < sb.length()) {
172-
if (m.find(j)) {
173-
// swap can change while this test is running
174-
if (!m.group(1).equals("swap")) {
175-
String name = f;
176-
if (name == null) {
177-
// cygwin's df lists windows path as FileSystem (1st group)
178-
name = Platform.isWindows() ? m.group(1) : m.group(5);
179-
}
180-
al.add(new Space(name, m.group(2), m.group(3), m.group(4)));
181-
}
182-
j = m.end();
183-
} else {
184-
throw new RuntimeException("unrecognized df output format: "
185-
+ "charAt(" + j + ") = '"
186-
+ sb.charAt(j) + "'");
187-
}
188-
}
189-
190-
if (al.size() == 0) {
191-
// df did not produce output
192-
String name = (f == null ? "" : f);
193-
al.add(new Space(name, "0", "0", "0"));
194-
}
195164
return al;
196165
}
197166

@@ -231,14 +200,15 @@ private static void compare(Space s) {
231200
long fs = f.getFreeSpace();
232201
long us = f.getUsableSpace();
233202

234-
out.format("%s:%n", s.name());
203+
out.format("%s (%d):%n", s.name(), s.size());
235204
String fmt = " %-4s total = %12d free = %12d usable = %12d%n";
236-
out.format(fmt, "df", s.total(), s.free(), s.available());
237-
out.format(fmt, "getX", ts, fs, us);
205+
out.format(fmt, "getSpace0", s.total(), s.free(), s.available());
206+
out.format(fmt, "getXSpace", ts, fs, us);
238207

239208
// If the file system can dynamically change size, this check will fail.
240209
// This can happen on macOS for the /dev files system.
241-
if (ts != s.total() && (!Platform.isOSX() || !s.name().equals("/dev"))) {
210+
if (ts != s.total()
211+
&& (!Platform.isOSX() || !s.name().equals("/dev"))) {
242212
long blockSize = 1;
243213
long numBlocks = 0;
244214
try {
@@ -255,47 +225,76 @@ private static void compare(Space s) {
255225
throw new RuntimeException(e);
256226
}
257227

258-
// On macOS, the number of 1024 byte blocks might be incorrectly
259-
// calculated by 'df' using integer division by 2 of the number of
260-
// 512 byte blocks, resulting in a size smaller than the actual
261-
// value when the number of blocks is odd.
262-
if (!Platform.isOSX() || blockSize != 512 || numBlocks % 2 == 0 ||
263-
ts - s.total() != 512) {
264-
if (Platform.isWindows()) {
265-
//
266-
// In Cygwin, 'df' has been observed to account for quotas
267-
// when reporting the total disk size, but the total size
268-
// reported by GetDiskFreeSpaceExW() has been observed not
269-
// to account for the quota in which case the latter value
270-
// should be larger.
271-
//
272-
if (s.total() > ts) {
273-
fail(s.name() + " total space", s.total(), ">", ts);
274-
}
275-
} else {
276-
fail(s.name() + " total space", s.total(), "!=", ts);
228+
if (Platform.isWindows()) {
229+
if (ts > s.total()) {
230+
fail(s.name() + " total space", ts, ">", s.total());
277231
}
232+
} else if (ts != s.total()) {
233+
fail(s.name() + " total space", ts, "!=", s.total());
278234
}
279235
} else {
280236
pass();
281237
}
282238

283-
// unix df returns statvfs.f_bavail
239+
// unix usable space is from statvfs.f_bavail
284240
long tsp = (!Platform.isWindows() ? us : fs);
285241
if (!s.woomFree(tsp)) {
286242
fail(s.name(), s.available(), "??", tsp);
287243
} else {
288244
pass();
289245
}
290246

291-
if (fs > s.total()) {
292-
fail(s.name(), s.total(), ">", fs);
247+
//
248+
// Invariants are:
249+
// total space <= size
250+
// total space == size (Unix)
251+
// free space <= total space (if no quotas in effect) (Windows)
252+
// free space < size (if quotas in effect) (Windows)
253+
// usable space <= total space
254+
// usable space <= free space
255+
//
256+
257+
// total space <= size
258+
if (ts > s.size()) {
259+
fail(s.name() + " size", ts, ">", s.size());
293260
} else {
294261
pass();
295262
}
296263

264+
// On Unix the total space should always be the volume size
265+
if (Platform.isWindows()) {
266+
// ts != s.size() indicates that quotas are in effect
267+
if (ts == s.size() && fs > s.total()) {
268+
fail(s.name() + " free space", fs, ">", s.total());
269+
} else if (ts < s.size() && fs > s.size()) {
270+
fail(s.name() + " free space (quota)", fs, ">", s.size());
271+
} else {
272+
pass();
273+
}
274+
} else { // not Windows
275+
if (ts != s.size()) {
276+
fail(s.name() + " total space", ts, "!=", s.size());
277+
} else {
278+
pass();
279+
}
280+
}
281+
282+
// usable space <= total space
297283
if (us > s.total()) {
298-
fail(s.name(), s.total(), ">", us);
284+
fail(s.name() + " usable space", us, ">", s.total());
285+
} else {
286+
pass();
287+
}
288+
289+
// usable space <= free space
290+
if (us > s.free()) {
291+
// free and usable change dynamically
292+
System.err.println("Warning: us > s.free()");
293+
if (1.0 - Math.abs((double)s.free()/(double)us) > 0.01) {
294+
fail(s.name() + " usable vs. free space", us, ">", s.free());
295+
} else {
296+
pass();
297+
}
299298
} else {
300299
pass();
301300
}
@@ -352,14 +351,14 @@ private static class Deny extends SecurityManager {
352351
public void checkPermission(Permission p) {
353352
if (p.implies(new RuntimePermission("setSecurityManager"))
354353
|| p.implies(new RuntimePermission("getProtectionDomain")))
355-
return;
354+
return;
356355
super.checkPermission(p);
357356
}
358357

359358
public void checkPermission(Permission p, Object context) {
360359
if (p.implies(new RuntimePermission("setSecurityManager"))
361360
|| p.implies(new RuntimePermission("getProtectionDomain")))
362-
return;
361+
return;
363362
super.checkPermission(p, context);
364363
}
365364
}
@@ -391,31 +390,25 @@ public void checkRead(String file) {
391390
private static int testFile(Path dir) {
392391
String dirName = dir.toString();
393392
out.format("--- Testing %s%n", dirName);
394-
ArrayList<Space> l;
395-
try {
396-
l = space(dirName);
397-
} catch (IOException x) {
398-
throw new RuntimeException(dirName + " can't get file system information", x);
399-
}
400-
compare(l.get(0));
393+
compare(new Space(dir.getRoot().toString()));
401394

402395
if (fail != 0) {
403396
err.format("%d tests: %d failure(s); first: %s%n",
404-
fail + pass, fail, first);
397+
fail + pass, fail, first);
405398
} else {
406399
out.format("all %d tests passed%n", fail + pass);
407400
}
408401

409402
return fail != 0 ? 1 : 0;
410403
}
411404

412-
private static int testDF() {
413-
out.println("--- Testing df");
414-
// Find all of the partitions on the machine and verify that the size
415-
// returned by "df" is equivalent to File.getXSpace() values.
416-
ArrayList<Space> l;
405+
private static int testVolumes() {
406+
out.println("--- Testing volumes");
407+
// Find all of the partitions on the machine and verify that the sizes
408+
// returned by File::getXSpace are equivalent to those from getSpace0
409+
ArrayList<String> l;
417410
try {
418-
l = space(null);
411+
l = paths();
419412
if (Platform.isWindows()) {
420413
diskFree();
421414
}
@@ -434,7 +427,8 @@ private static int testDF() {
434427

435428
out.format("%nSecurityManager = %s%n" ,
436429
(sm == null ? "null" : sm.getClass().getName()));
437-
for (var s : l) {
430+
for (var p : l) {
431+
Space s = new Space(p);
438432
if (sm instanceof Deny) {
439433
tryCatch(s);
440434
} else {
@@ -449,7 +443,7 @@ private static int testDF() {
449443

450444
if (fail != 0) {
451445
err.format("%d tests: %d failure(s); first: %s%n",
452-
fail + pass, fail, first);
446+
fail + pass, fail, first);
453447
} else {
454448
out.format("all %d tests passed%n", fail + pass);
455449
}
@@ -472,7 +466,7 @@ private static void allow(Path path) throws IOException {
472466
}
473467

474468
public static void main(String[] args) throws Exception {
475-
int failedTests = testDF();
469+
int failedTests = testVolumes();
476470
reset();
477471

478472
Path tmpDir = Files.createTempDirectory(null);
@@ -491,4 +485,13 @@ public static void main(String[] args) throws Exception {
491485
throw new RuntimeException(failedTests + " test(s) failed");
492486
}
493487
}
488+
489+
//
490+
// root the root of the volume
491+
// size[0] total size: number of bytes in the volume
492+
// size[1] total space: number of bytes visible to the caller
493+
// size[2] free space: number of free bytes in the volume
494+
// size[3] usable space: number of bytes available to the caller
495+
//
496+
private static native boolean getSpace0(String root, long[] space);
494497
}

0 commit comments

Comments
 (0)