1
1
/*
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.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* This code is free software; you can redistribute it and/or modify it
29
29
* @summary Basic functionality of File.get-X-Space methods.
30
30
* @library .. /test/lib
31
31
* @build jdk.test.lib.Platform
32
- * @run main/othervm -Djava.security.manager=allow GetXSpace
32
+ * @run main/othervm/native -Djava.security.manager=allow GetXSpace
33
33
*/
34
34
35
35
import java .io .BufferedReader ;
52
52
53
53
@ SuppressWarnings ("removal" )
54
54
public class GetXSpace {
55
+ static {
56
+ System .loadLibrary ("GetXSpace" );
57
+ }
55
58
56
59
private static SecurityManager [] sma = { null , new Allow (), new DenyFSA (),
57
60
new DenyRead () };
58
61
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
-
62
62
private static int fail = 0 ;
63
63
private static int pass = 0 ;
64
64
private static Throwable first ;
@@ -100,34 +100,36 @@ private static void setFirst(String s) {
100
100
}
101
101
102
102
private static class Space {
103
- private static final long KSIZE = 1024 ;
104
103
private final String name ;
104
+ private final long size ;
105
105
private final long total ;
106
- private final long used ;
106
+ private final long free ;
107
107
private final long available ;
108
108
109
- Space (String name , String total , String used , String available ) {
109
+ Space (String name ) {
110
110
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 ];
118
118
}
119
119
120
120
String name () { return name ; }
121
+ long size () { return size ; }
121
122
long total () { return total ; }
122
- long used () { return used ; }
123
123
long available () { return available ; }
124
- long free () { return total - used ; }
124
+ long free () { return free ; }
125
+
125
126
boolean woomFree (long freeSpace ) {
126
127
return ((freeSpace >= (available / 10 )) &&
127
128
(freeSpace <= (available * 10 )));
128
129
}
130
+
129
131
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 );
131
133
}
132
134
}
133
135
@@ -149,49 +151,16 @@ private static void diskFree() throws IOException {
149
151
out .println (sb );
150
152
}
151
153
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 <>();
154
156
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 );
166
162
}
167
- out .println (sb );
168
163
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
- }
195
164
return al ;
196
165
}
197
166
@@ -231,14 +200,15 @@ private static void compare(Space s) {
231
200
long fs = f .getFreeSpace ();
232
201
long us = f .getUsableSpace ();
233
202
234
- out .format ("%s:%n" , s .name ());
203
+ out .format ("%s (%d) :%n" , s .name (), s . size ());
235
204
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 );
238
207
239
208
// If the file system can dynamically change size, this check will fail.
240
209
// 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" ))) {
242
212
long blockSize = 1 ;
243
213
long numBlocks = 0 ;
244
214
try {
@@ -255,47 +225,76 @@ private static void compare(Space s) {
255
225
throw new RuntimeException (e );
256
226
}
257
227
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 ());
277
231
}
232
+ } else if (ts != s .total ()) {
233
+ fail (s .name () + " total space" , ts , "!=" , s .total ());
278
234
}
279
235
} else {
280
236
pass ();
281
237
}
282
238
283
- // unix df returns statvfs.f_bavail
239
+ // unix usable space is from statvfs.f_bavail
284
240
long tsp = (!Platform .isWindows () ? us : fs );
285
241
if (!s .woomFree (tsp )) {
286
242
fail (s .name (), s .available (), "??" , tsp );
287
243
} else {
288
244
pass ();
289
245
}
290
246
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 ());
293
260
} else {
294
261
pass ();
295
262
}
296
263
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
297
283
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
+ }
299
298
} else {
300
299
pass ();
301
300
}
@@ -352,14 +351,14 @@ private static class Deny extends SecurityManager {
352
351
public void checkPermission (Permission p ) {
353
352
if (p .implies (new RuntimePermission ("setSecurityManager" ))
354
353
|| p .implies (new RuntimePermission ("getProtectionDomain" )))
355
- return ;
354
+ return ;
356
355
super .checkPermission (p );
357
356
}
358
357
359
358
public void checkPermission (Permission p , Object context ) {
360
359
if (p .implies (new RuntimePermission ("setSecurityManager" ))
361
360
|| p .implies (new RuntimePermission ("getProtectionDomain" )))
362
- return ;
361
+ return ;
363
362
super .checkPermission (p , context );
364
363
}
365
364
}
@@ -391,31 +390,25 @@ public void checkRead(String file) {
391
390
private static int testFile (Path dir ) {
392
391
String dirName = dir .toString ();
393
392
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 ()));
401
394
402
395
if (fail != 0 ) {
403
396
err .format ("%d tests: %d failure(s); first: %s%n" ,
404
- fail + pass , fail , first );
397
+ fail + pass , fail , first );
405
398
} else {
406
399
out .format ("all %d tests passed%n" , fail + pass );
407
400
}
408
401
409
402
return fail != 0 ? 1 : 0 ;
410
403
}
411
404
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 ;
417
410
try {
418
- l = space ( null );
411
+ l = paths ( );
419
412
if (Platform .isWindows ()) {
420
413
diskFree ();
421
414
}
@@ -434,7 +427,8 @@ private static int testDF() {
434
427
435
428
out .format ("%nSecurityManager = %s%n" ,
436
429
(sm == null ? "null" : sm .getClass ().getName ()));
437
- for (var s : l ) {
430
+ for (var p : l ) {
431
+ Space s = new Space (p );
438
432
if (sm instanceof Deny ) {
439
433
tryCatch (s );
440
434
} else {
@@ -449,7 +443,7 @@ private static int testDF() {
449
443
450
444
if (fail != 0 ) {
451
445
err .format ("%d tests: %d failure(s); first: %s%n" ,
452
- fail + pass , fail , first );
446
+ fail + pass , fail , first );
453
447
} else {
454
448
out .format ("all %d tests passed%n" , fail + pass );
455
449
}
@@ -472,7 +466,7 @@ private static void allow(Path path) throws IOException {
472
466
}
473
467
474
468
public static void main (String [] args ) throws Exception {
475
- int failedTests = testDF ();
469
+ int failedTests = testVolumes ();
476
470
reset ();
477
471
478
472
Path tmpDir = Files .createTempDirectory (null );
@@ -491,4 +485,13 @@ public static void main(String[] args) throws Exception {
491
485
throw new RuntimeException (failedTests + " test(s) failed" );
492
486
}
493
487
}
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 );
494
497
}
0 commit comments