Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openjdk-multiarch-jdk8u
Path: blob/aarch64-shenandoah-jdk8u272-b10/jdk/src/windows/classes/java/io/WinNTFileSystem.java
32287 views
1
/*
2
* Copyright (c) 2001, 2012, 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. Oracle designates this
8
* particular file as subject to the "Classpath" exception as provided
9
* by Oracle in the LICENSE file that accompanied this code.
10
*
11
* This code is distributed in the hope that it will be useful, but WITHOUT
12
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14
* version 2 for more details (a copy is included in the LICENSE file that
15
* accompanied this code).
16
*
17
* You should have received a copy of the GNU General Public License version
18
* 2 along with this work; if not, write to the Free Software Foundation,
19
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20
*
21
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22
* or visit www.oracle.com if you need additional information or have any
23
* questions.
24
*/
25
26
package java.io;
27
28
import java.security.AccessController;
29
import java.util.Locale;
30
import sun.security.action.GetPropertyAction;
31
32
/**
33
* Unicode-aware FileSystem for Windows NT/2000.
34
*
35
* @author Konstantin Kladko
36
* @since 1.4
37
*/
38
class WinNTFileSystem extends FileSystem {
39
40
private final char slash;
41
private final char altSlash;
42
private final char semicolon;
43
44
public WinNTFileSystem() {
45
slash = AccessController.doPrivileged(
46
new GetPropertyAction("file.separator")).charAt(0);
47
semicolon = AccessController.doPrivileged(
48
new GetPropertyAction("path.separator")).charAt(0);
49
altSlash = (this.slash == '\\') ? '/' : '\\';
50
}
51
52
private boolean isSlash(char c) {
53
return (c == '\\') || (c == '/');
54
}
55
56
private boolean isLetter(char c) {
57
return ((c >= 'a') && (c <= 'z')) || ((c >= 'A') && (c <= 'Z'));
58
}
59
60
private String slashify(String p) {
61
if ((p.length() > 0) && (p.charAt(0) != slash)) return slash + p;
62
else return p;
63
}
64
65
/* -- Normalization and construction -- */
66
67
@Override
68
public char getSeparator() {
69
return slash;
70
}
71
72
@Override
73
public char getPathSeparator() {
74
return semicolon;
75
}
76
77
/* Check that the given pathname is normal. If not, invoke the real
78
normalizer on the part of the pathname that requires normalization.
79
This way we iterate through the whole pathname string only once. */
80
@Override
81
public String normalize(String path) {
82
int n = path.length();
83
char slash = this.slash;
84
char altSlash = this.altSlash;
85
char prev = 0;
86
for (int i = 0; i < n; i++) {
87
char c = path.charAt(i);
88
if (c == altSlash)
89
return normalize(path, n, (prev == slash) ? i - 1 : i);
90
if ((c == slash) && (prev == slash) && (i > 1))
91
return normalize(path, n, i - 1);
92
if ((c == ':') && (i > 1))
93
return normalize(path, n, 0);
94
prev = c;
95
}
96
if (prev == slash) return normalize(path, n, n - 1);
97
return path;
98
}
99
100
/* Normalize the given pathname, whose length is len, starting at the given
101
offset; everything before this offset is already normal. */
102
private String normalize(String path, int len, int off) {
103
if (len == 0) return path;
104
if (off < 3) off = 0; /* Avoid fencepost cases with UNC pathnames */
105
int src;
106
char slash = this.slash;
107
StringBuffer sb = new StringBuffer(len);
108
109
if (off == 0) {
110
/* Complete normalization, including prefix */
111
src = normalizePrefix(path, len, sb);
112
} else {
113
/* Partial normalization */
114
src = off;
115
sb.append(path.substring(0, off));
116
}
117
118
/* Remove redundant slashes from the remainder of the path, forcing all
119
slashes into the preferred slash */
120
while (src < len) {
121
char c = path.charAt(src++);
122
if (isSlash(c)) {
123
while ((src < len) && isSlash(path.charAt(src))) src++;
124
if (src == len) {
125
/* Check for trailing separator */
126
int sn = sb.length();
127
if ((sn == 2) && (sb.charAt(1) == ':')) {
128
/* "z:\\" */
129
sb.append(slash);
130
break;
131
}
132
if (sn == 0) {
133
/* "\\" */
134
sb.append(slash);
135
break;
136
}
137
if ((sn == 1) && (isSlash(sb.charAt(0)))) {
138
/* "\\\\" is not collapsed to "\\" because "\\\\" marks
139
the beginning of a UNC pathname. Even though it is
140
not, by itself, a valid UNC pathname, we leave it as
141
is in order to be consistent with the win32 APIs,
142
which treat this case as an invalid UNC pathname
143
rather than as an alias for the root directory of
144
the current drive. */
145
sb.append(slash);
146
break;
147
}
148
/* Path does not denote a root directory, so do not append
149
trailing slash */
150
break;
151
} else {
152
sb.append(slash);
153
}
154
} else {
155
sb.append(c);
156
}
157
}
158
159
String rv = sb.toString();
160
return rv;
161
}
162
163
/* A normal Win32 pathname contains no duplicate slashes, except possibly
164
for a UNC prefix, and does not end with a slash. It may be the empty
165
string. Normalized Win32 pathnames have the convenient property that
166
the length of the prefix almost uniquely identifies the type of the path
167
and whether it is absolute or relative:
168
169
0 relative to both drive and directory
170
1 drive-relative (begins with '\\')
171
2 absolute UNC (if first char is '\\'),
172
else directory-relative (has form "z:foo")
173
3 absolute local pathname (begins with "z:\\")
174
*/
175
private int normalizePrefix(String path, int len, StringBuffer sb) {
176
int src = 0;
177
while ((src < len) && isSlash(path.charAt(src))) src++;
178
char c;
179
if ((len - src >= 2)
180
&& isLetter(c = path.charAt(src))
181
&& path.charAt(src + 1) == ':') {
182
/* Remove leading slashes if followed by drive specifier.
183
This hack is necessary to support file URLs containing drive
184
specifiers (e.g., "file://c:/path"). As a side effect,
185
"/c:/path" can be used as an alternative to "c:/path". */
186
sb.append(c);
187
sb.append(':');
188
src += 2;
189
} else {
190
src = 0;
191
if ((len >= 2)
192
&& isSlash(path.charAt(0))
193
&& isSlash(path.charAt(1))) {
194
/* UNC pathname: Retain first slash; leave src pointed at
195
second slash so that further slashes will be collapsed
196
into the second slash. The result will be a pathname
197
beginning with "\\\\" followed (most likely) by a host
198
name. */
199
src = 1;
200
sb.append(slash);
201
}
202
}
203
return src;
204
}
205
206
@Override
207
public int prefixLength(String path) {
208
char slash = this.slash;
209
int n = path.length();
210
if (n == 0) return 0;
211
char c0 = path.charAt(0);
212
char c1 = (n > 1) ? path.charAt(1) : 0;
213
if (c0 == slash) {
214
if (c1 == slash) return 2; /* Absolute UNC pathname "\\\\foo" */
215
return 1; /* Drive-relative "\\foo" */
216
}
217
if (isLetter(c0) && (c1 == ':')) {
218
if ((n > 2) && (path.charAt(2) == slash))
219
return 3; /* Absolute local pathname "z:\\foo" */
220
return 2; /* Directory-relative "z:foo" */
221
}
222
return 0; /* Completely relative */
223
}
224
225
@Override
226
public String resolve(String parent, String child) {
227
int pn = parent.length();
228
if (pn == 0) return child;
229
int cn = child.length();
230
if (cn == 0) return parent;
231
232
String c = child;
233
int childStart = 0;
234
int parentEnd = pn;
235
236
if ((cn > 1) && (c.charAt(0) == slash)) {
237
if (c.charAt(1) == slash) {
238
/* Drop prefix when child is a UNC pathname */
239
childStart = 2;
240
} else {
241
/* Drop prefix when child is drive-relative */
242
childStart = 1;
243
244
}
245
if (cn == childStart) { // Child is double slash
246
if (parent.charAt(pn - 1) == slash)
247
return parent.substring(0, pn - 1);
248
return parent;
249
}
250
}
251
252
if (parent.charAt(pn - 1) == slash)
253
parentEnd--;
254
255
int strlen = parentEnd + cn - childStart;
256
char[] theChars = null;
257
if (child.charAt(childStart) == slash) {
258
theChars = new char[strlen];
259
parent.getChars(0, parentEnd, theChars, 0);
260
child.getChars(childStart, cn, theChars, parentEnd);
261
} else {
262
theChars = new char[strlen + 1];
263
parent.getChars(0, parentEnd, theChars, 0);
264
theChars[parentEnd] = slash;
265
child.getChars(childStart, cn, theChars, parentEnd + 1);
266
}
267
return new String(theChars);
268
}
269
270
@Override
271
public String getDefaultParent() {
272
return ("" + slash);
273
}
274
275
@Override
276
public String fromURIPath(String path) {
277
String p = path;
278
if ((p.length() > 2) && (p.charAt(2) == ':')) {
279
// "/c:/foo" --> "c:/foo"
280
p = p.substring(1);
281
// "c:/foo/" --> "c:/foo", but "c:/" --> "c:/"
282
if ((p.length() > 3) && p.endsWith("/"))
283
p = p.substring(0, p.length() - 1);
284
} else if ((p.length() > 1) && p.endsWith("/")) {
285
// "/foo/" --> "/foo"
286
p = p.substring(0, p.length() - 1);
287
}
288
return p;
289
}
290
291
/* -- Path operations -- */
292
293
@Override
294
public boolean isAbsolute(File f) {
295
int pl = f.getPrefixLength();
296
return (((pl == 2) && (f.getPath().charAt(0) == slash))
297
|| (pl == 3));
298
}
299
300
@Override
301
public String resolve(File f) {
302
String path = f.getPath();
303
int pl = f.getPrefixLength();
304
if ((pl == 2) && (path.charAt(0) == slash))
305
return path; /* UNC */
306
if (pl == 3)
307
return path; /* Absolute local */
308
if (pl == 0)
309
return getUserPath() + slashify(path); /* Completely relative */
310
if (pl == 1) { /* Drive-relative */
311
String up = getUserPath();
312
String ud = getDrive(up);
313
if (ud != null) return ud + path;
314
return up + path; /* User dir is a UNC path */
315
}
316
if (pl == 2) { /* Directory-relative */
317
String up = getUserPath();
318
String ud = getDrive(up);
319
if ((ud != null) && path.startsWith(ud))
320
return up + slashify(path.substring(2));
321
char drive = path.charAt(0);
322
String dir = getDriveDirectory(drive);
323
String np;
324
if (dir != null) {
325
/* When resolving a directory-relative path that refers to a
326
drive other than the current drive, insist that the caller
327
have read permission on the result */
328
String p = drive + (':' + dir + slashify(path.substring(2)));
329
SecurityManager security = System.getSecurityManager();
330
try {
331
if (security != null) security.checkRead(p);
332
} catch (SecurityException x) {
333
/* Don't disclose the drive's directory in the exception */
334
throw new SecurityException("Cannot resolve path " + path);
335
}
336
return p;
337
}
338
return drive + ":" + slashify(path.substring(2)); /* fake it */
339
}
340
throw new InternalError("Unresolvable path: " + path);
341
}
342
343
private String getUserPath() {
344
/* For both compatibility and security,
345
we must look this up every time */
346
return normalize(System.getProperty("user.dir"));
347
}
348
349
private String getDrive(String path) {
350
int pl = prefixLength(path);
351
return (pl == 3) ? path.substring(0, 2) : null;
352
}
353
354
private static String[] driveDirCache = new String[26];
355
356
private static int driveIndex(char d) {
357
if ((d >= 'a') && (d <= 'z')) return d - 'a';
358
if ((d >= 'A') && (d <= 'Z')) return d - 'A';
359
return -1;
360
}
361
362
private native String getDriveDirectory(int drive);
363
364
private String getDriveDirectory(char drive) {
365
int i = driveIndex(drive);
366
if (i < 0) return null;
367
String s = driveDirCache[i];
368
if (s != null) return s;
369
s = getDriveDirectory(i + 1);
370
driveDirCache[i] = s;
371
return s;
372
}
373
374
// Caches for canonicalization results to improve startup performance.
375
// The first cache handles repeated canonicalizations of the same path
376
// name. The prefix cache handles repeated canonicalizations within the
377
// same directory, and must not create results differing from the true
378
// canonicalization algorithm in canonicalize_md.c. For this reason the
379
// prefix cache is conservative and is not used for complex path names.
380
private ExpiringCache cache = new ExpiringCache();
381
private ExpiringCache prefixCache = new ExpiringCache();
382
383
@Override
384
public String canonicalize(String path) throws IOException {
385
// If path is a drive letter only then skip canonicalization
386
int len = path.length();
387
if ((len == 2) &&
388
(isLetter(path.charAt(0))) &&
389
(path.charAt(1) == ':')) {
390
char c = path.charAt(0);
391
if ((c >= 'A') && (c <= 'Z'))
392
return path;
393
return "" + ((char) (c-32)) + ':';
394
} else if ((len == 3) &&
395
(isLetter(path.charAt(0))) &&
396
(path.charAt(1) == ':') &&
397
(path.charAt(2) == '\\')) {
398
char c = path.charAt(0);
399
if ((c >= 'A') && (c <= 'Z'))
400
return path;
401
return "" + ((char) (c-32)) + ':' + '\\';
402
}
403
if (!useCanonCaches) {
404
return canonicalize0(path);
405
} else {
406
String res = cache.get(path);
407
if (res == null) {
408
String dir = null;
409
String resDir = null;
410
if (useCanonPrefixCache) {
411
dir = parentOrNull(path);
412
if (dir != null) {
413
resDir = prefixCache.get(dir);
414
if (resDir != null) {
415
/*
416
* Hit only in prefix cache; full path is canonical,
417
* but we need to get the canonical name of the file
418
* in this directory to get the appropriate
419
* capitalization
420
*/
421
String filename = path.substring(1 + dir.length());
422
res = canonicalizeWithPrefix(resDir, filename);
423
cache.put(dir + File.separatorChar + filename, res);
424
}
425
}
426
}
427
if (res == null) {
428
res = canonicalize0(path);
429
cache.put(path, res);
430
if (useCanonPrefixCache && dir != null) {
431
resDir = parentOrNull(res);
432
if (resDir != null) {
433
File f = new File(res);
434
if (f.exists() && !f.isDirectory()) {
435
prefixCache.put(dir, resDir);
436
}
437
}
438
}
439
}
440
}
441
return res;
442
}
443
}
444
445
private native String canonicalize0(String path)
446
throws IOException;
447
448
private String canonicalizeWithPrefix(String canonicalPrefix,
449
String filename) throws IOException
450
{
451
return canonicalizeWithPrefix0(canonicalPrefix,
452
canonicalPrefix + File.separatorChar + filename);
453
}
454
455
// Run the canonicalization operation assuming that the prefix
456
// (everything up to the last filename) is canonical; just gets
457
// the canonical name of the last element of the path
458
private native String canonicalizeWithPrefix0(String canonicalPrefix,
459
String pathWithCanonicalPrefix)
460
throws IOException;
461
462
// Best-effort attempt to get parent of this path; used for
463
// optimization of filename canonicalization. This must return null for
464
// any cases where the code in canonicalize_md.c would throw an
465
// exception or otherwise deal with non-simple pathnames like handling
466
// of "." and "..". It may conservatively return null in other
467
// situations as well. Returning null will cause the underlying
468
// (expensive) canonicalization routine to be called.
469
private static String parentOrNull(String path) {
470
if (path == null) return null;
471
char sep = File.separatorChar;
472
char altSep = '/';
473
int last = path.length() - 1;
474
int idx = last;
475
int adjacentDots = 0;
476
int nonDotCount = 0;
477
while (idx > 0) {
478
char c = path.charAt(idx);
479
if (c == '.') {
480
if (++adjacentDots >= 2) {
481
// Punt on pathnames containing . and ..
482
return null;
483
}
484
if (nonDotCount == 0) {
485
// Punt on pathnames ending in a .
486
return null;
487
}
488
} else if (c == sep) {
489
if (adjacentDots == 1 && nonDotCount == 0) {
490
// Punt on pathnames containing . and ..
491
return null;
492
}
493
if (idx == 0 ||
494
idx >= last - 1 ||
495
path.charAt(idx - 1) == sep ||
496
path.charAt(idx - 1) == altSep) {
497
// Punt on pathnames containing adjacent slashes
498
// toward the end
499
return null;
500
}
501
return path.substring(0, idx);
502
} else if (c == altSep) {
503
// Punt on pathnames containing both backward and
504
// forward slashes
505
return null;
506
} else if (c == '*' || c == '?') {
507
// Punt on pathnames containing wildcards
508
return null;
509
} else {
510
++nonDotCount;
511
adjacentDots = 0;
512
}
513
--idx;
514
}
515
return null;
516
}
517
518
/* -- Attribute accessors -- */
519
520
@Override
521
public native int getBooleanAttributes(File f);
522
523
@Override
524
public native boolean checkAccess(File f, int access);
525
526
@Override
527
public native long getLastModifiedTime(File f);
528
529
@Override
530
public native long getLength(File f);
531
532
@Override
533
public native boolean setPermission(File f, int access, boolean enable,
534
boolean owneronly);
535
536
/* -- File operations -- */
537
538
@Override
539
public native boolean createFileExclusively(String path)
540
throws IOException;
541
542
@Override
543
public native String[] list(File f);
544
545
@Override
546
public native boolean createDirectory(File f);
547
548
@Override
549
public native boolean setLastModifiedTime(File f, long time);
550
551
@Override
552
public native boolean setReadOnly(File f);
553
554
@Override
555
public boolean delete(File f) {
556
// Keep canonicalization caches in sync after file deletion
557
// and renaming operations. Could be more clever than this
558
// (i.e., only remove/update affected entries) but probably
559
// not worth it since these entries expire after 30 seconds
560
// anyway.
561
cache.clear();
562
prefixCache.clear();
563
return delete0(f);
564
}
565
566
private native boolean delete0(File f);
567
568
@Override
569
public boolean rename(File f1, File f2) {
570
// Keep canonicalization caches in sync after file deletion
571
// and renaming operations. Could be more clever than this
572
// (i.e., only remove/update affected entries) but probably
573
// not worth it since these entries expire after 30 seconds
574
// anyway.
575
cache.clear();
576
prefixCache.clear();
577
return rename0(f1, f2);
578
}
579
580
private native boolean rename0(File f1, File f2);
581
582
/* -- Filesystem interface -- */
583
584
@Override
585
public File[] listRoots() {
586
int ds = listRoots0();
587
int n = 0;
588
for (int i = 0; i < 26; i++) {
589
if (((ds >> i) & 1) != 0) {
590
if (!access((char)('A' + i) + ":" + slash))
591
ds &= ~(1 << i);
592
else
593
n++;
594
}
595
}
596
File[] fs = new File[n];
597
int j = 0;
598
char slash = this.slash;
599
for (int i = 0; i < 26; i++) {
600
if (((ds >> i) & 1) != 0)
601
fs[j++] = new File((char)('A' + i) + ":" + slash);
602
}
603
return fs;
604
}
605
606
private static native int listRoots0();
607
608
private boolean access(String path) {
609
try {
610
SecurityManager security = System.getSecurityManager();
611
if (security != null) security.checkRead(path);
612
return true;
613
} catch (SecurityException x) {
614
return false;
615
}
616
}
617
618
/* -- Disk usage -- */
619
620
@Override
621
public long getSpace(File f, int t) {
622
if (f.exists()) {
623
return getSpace0(f, t);
624
}
625
return 0;
626
}
627
628
private native long getSpace0(File f, int t);
629
630
/* -- Basic infrastructure -- */
631
632
@Override
633
public int compare(File f1, File f2) {
634
return f1.getPath().compareToIgnoreCase(f2.getPath());
635
}
636
637
@Override
638
public int hashCode(File f) {
639
/* Could make this more efficient: String.hashCodeIgnoreCase */
640
return f.getPath().toLowerCase(Locale.ENGLISH).hashCode() ^ 1234321;
641
}
642
643
private static native void initIDs();
644
645
static {
646
initIDs();
647
}
648
}
649
650