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