Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.base/windows/classes/sun/nio/fs/WindowsLinkSupport.java
41139 views
1
/*
2
* Copyright (c) 2008, 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 sun.nio.fs;
27
28
import java.nio.file.*;
29
import java.io.IOException;
30
import java.io.IOError;
31
import java.security.AccessController;
32
import java.security.PrivilegedAction;
33
import jdk.internal.misc.Unsafe;
34
35
import static sun.nio.fs.WindowsNativeDispatcher.*;
36
import static sun.nio.fs.WindowsConstants.*;
37
38
/**
39
* Utility methods for symbolic link support on Windows Vista and newer.
40
*/
41
42
class WindowsLinkSupport {
43
private static final Unsafe unsafe = Unsafe.getUnsafe();
44
45
private WindowsLinkSupport() {
46
}
47
48
/**
49
* Returns the target of a symbolic link
50
*/
51
static String readLink(WindowsPath path) throws IOException {
52
long handle = 0L;
53
try {
54
handle = path.openForReadAttributeAccess(false); // don't follow links
55
} catch (WindowsException x) {
56
x.rethrowAsIOException(path);
57
}
58
try {
59
return readLinkImpl(handle);
60
} finally {
61
CloseHandle(handle);
62
}
63
}
64
65
/**
66
* Returns the final path (all symbolic links resolved) or null if this
67
* operation is not supported.
68
*/
69
static String getFinalPath(WindowsPath input) throws IOException {
70
long h = 0;
71
try {
72
h = input.openForReadAttributeAccess(true);
73
} catch (WindowsException x) {
74
x.rethrowAsIOException(input);
75
}
76
try {
77
return stripPrefix(GetFinalPathNameByHandle(h));
78
} catch (WindowsException x) {
79
// ERROR_INVALID_LEVEL is the error returned when not supported
80
// (a sym link to file on FAT32 or Samba server for example)
81
if (x.lastError() != ERROR_INVALID_LEVEL)
82
x.rethrowAsIOException(input);
83
} finally {
84
CloseHandle(h);
85
}
86
return null;
87
}
88
89
/**
90
* Returns the final path of a given path as a String. This should be used
91
* prior to calling Win32 system calls that do not follow links.
92
*/
93
@SuppressWarnings("removal")
94
static String getFinalPath(WindowsPath input, boolean followLinks)
95
throws IOException
96
{
97
WindowsFileSystem fs = input.getFileSystem();
98
try {
99
// if not following links then don't need final path
100
if (!followLinks)
101
return input.getPathForWin32Calls();
102
103
// if file is not a sym link then don't need final path
104
if (!WindowsFileAttributes.get(input, false).isSymbolicLink()) {
105
return input.getPathForWin32Calls();
106
}
107
} catch (WindowsException x) {
108
x.rethrowAsIOException(input);
109
}
110
111
// The file is a symbolic link so attempt to get the final path
112
String result = getFinalPath(input);
113
if (result != null)
114
return result;
115
116
// Fallback: read target of link, resolve against parent, and repeat
117
// until file is not a link.
118
WindowsPath target = input;
119
int linkCount = 0;
120
do {
121
try {
122
WindowsFileAttributes attrs =
123
WindowsFileAttributes.get(target, false);
124
// non a link so we are done
125
if (!attrs.isSymbolicLink()) {
126
return target.getPathForWin32Calls();
127
}
128
} catch (WindowsException x) {
129
x.rethrowAsIOException(target);
130
}
131
WindowsPath link = WindowsPath
132
.createFromNormalizedPath(fs, readLink(target));
133
WindowsPath parent = target.getParent();
134
if (parent == null) {
135
// no parent so use parent of absolute path
136
final WindowsPath t = target;
137
target = AccessController
138
.doPrivileged(new PrivilegedAction<WindowsPath>() {
139
@Override
140
public WindowsPath run() {
141
return t.toAbsolutePath();
142
}});
143
parent = target.getParent();
144
}
145
target = parent.resolve(link);
146
147
} while (++linkCount < 32);
148
149
throw new FileSystemException(input.getPathForExceptionMessage(), null,
150
"Too many links");
151
}
152
153
/**
154
* Returns the actual path of a file, optionally resolving all symbolic
155
* links.
156
*/
157
static String getRealPath(WindowsPath input, boolean resolveLinks)
158
throws IOException
159
{
160
WindowsFileSystem fs = input.getFileSystem();
161
162
// Start with absolute path
163
String path = null;
164
try {
165
path = input.toAbsolutePath().toString();
166
} catch (IOError x) {
167
throw (IOException)(x.getCause());
168
}
169
170
// Collapse "." and ".."
171
if (path.indexOf('.') >= 0) {
172
try {
173
path = GetFullPathName(path);
174
} catch (WindowsException x) {
175
x.rethrowAsIOException(input);
176
}
177
}
178
179
// string builder to build up components of path
180
StringBuilder sb = new StringBuilder(path.length());
181
182
// Copy root component
183
int start;
184
char c0 = path.charAt(0);
185
char c1 = path.charAt(1);
186
if ((c0 <= 'z' && c0 >= 'a' || c0 <= 'Z' && c0 >= 'A') &&
187
c1 == ':' && path.charAt(2) == '\\') {
188
// Driver specifier
189
sb.append(Character.toUpperCase(c0));
190
sb.append(":\\");
191
start = 3;
192
} else if (c0 == '\\' && c1 == '\\') {
193
// UNC pathname, begins with "\\\\host\\share"
194
int last = path.length() - 1;
195
int pos = path.indexOf('\\', 2);
196
// skip both server and share names
197
if (pos == -1 || (pos == last)) {
198
// The UNC does not have a share name (collapsed by GetFullPathName)
199
throw new FileSystemException(input.getPathForExceptionMessage(),
200
null, "UNC has invalid share");
201
}
202
pos = path.indexOf('\\', pos+1);
203
if (pos < 0) {
204
pos = last;
205
sb.append(path).append("\\");
206
} else {
207
sb.append(path, 0, pos+1);
208
}
209
start = pos + 1;
210
} else {
211
throw new AssertionError("path type not recognized");
212
}
213
214
// if the result is only a root component then we simply check it exists
215
if (start >= path.length()) {
216
String result = sb.toString();
217
try {
218
GetFileAttributes(result);
219
} catch (WindowsException x) {
220
x.rethrowAsIOException(path);
221
}
222
return result;
223
}
224
225
// iterate through each component to get its actual name in the
226
// directory
227
int curr = start;
228
while (curr < path.length()) {
229
int next = path.indexOf('\\', curr);
230
int end = (next == -1) ? path.length() : next;
231
String search = sb.toString() + path.substring(curr, end);
232
try {
233
FirstFile fileData = FindFirstFile(WindowsPath.addPrefixIfNeeded(search));
234
FindClose(fileData.handle());
235
236
// if a reparse point is encountered then we must return the
237
// final path.
238
if (resolveLinks &&
239
WindowsFileAttributes.isReparsePoint(fileData.attributes()))
240
{
241
String result = getFinalPath(input);
242
if (result == null) {
243
// Fallback to slow path, usually because there is a sym
244
// link to a file system that doesn't support sym links.
245
WindowsPath resolved = resolveAllLinks(
246
WindowsPath.createFromNormalizedPath(fs, path));
247
result = getRealPath(resolved, false);
248
}
249
return result;
250
}
251
252
// add the name to the result
253
sb.append(fileData.name());
254
if (next != -1) {
255
sb.append('\\');
256
}
257
} catch (WindowsException e) {
258
e.rethrowAsIOException(path);
259
}
260
curr = end + 1;
261
}
262
263
return sb.toString();
264
}
265
266
/**
267
* Returns target of a symbolic link given the handle of an open file
268
* (that should be a link).
269
*/
270
private static String readLinkImpl(long handle) throws IOException {
271
int size = MAXIMUM_REPARSE_DATA_BUFFER_SIZE;
272
NativeBuffer buffer = NativeBuffers.getNativeBuffer(size);
273
try {
274
try {
275
DeviceIoControlGetReparsePoint(handle, buffer.address(), size);
276
} catch (WindowsException x) {
277
// FIXME: exception doesn't have file name
278
if (x.lastError() == ERROR_NOT_A_REPARSE_POINT)
279
throw new NotLinkException(null, null, x.errorString());
280
x.rethrowAsIOException((String)null);
281
}
282
283
/*
284
* typedef struct _REPARSE_DATA_BUFFER {
285
* ULONG ReparseTag;
286
* USHORT ReparseDataLength;
287
* USHORT Reserved;
288
* union {
289
* struct {
290
* USHORT SubstituteNameOffset;
291
* USHORT SubstituteNameLength;
292
* USHORT PrintNameOffset;
293
* USHORT PrintNameLength;
294
* WCHAR PathBuffer[1];
295
* } SymbolicLinkReparseBuffer;
296
* struct {
297
* USHORT SubstituteNameOffset;
298
* USHORT SubstituteNameLength;
299
* USHORT PrintNameOffset;
300
* USHORT PrintNameLength;
301
* WCHAR PathBuffer[1];
302
* } MountPointReparseBuffer;
303
* struct {
304
* UCHAR DataBuffer[1];
305
* } GenericReparseBuffer;
306
* };
307
* } REPARSE_DATA_BUFFER
308
*/
309
final short OFFSETOF_REPARSETAG = 0;
310
final short OFFSETOF_PATHOFFSET = 8;
311
final short OFFSETOF_PATHLENGTH = 10;
312
final short OFFSETOF_PATHBUFFER = 16 + 4; // check this
313
314
int tag = (int)unsafe.getLong(buffer.address() + OFFSETOF_REPARSETAG);
315
if (tag != IO_REPARSE_TAG_SYMLINK) {
316
// FIXME: exception doesn't have file name
317
throw new NotLinkException(null, null, "Reparse point is not a symbolic link");
318
}
319
320
// get offset and length of target
321
short nameOffset = unsafe.getShort(buffer.address() + OFFSETOF_PATHOFFSET);
322
short nameLengthInBytes = unsafe.getShort(buffer.address() + OFFSETOF_PATHLENGTH);
323
if ((nameLengthInBytes % 2) != 0)
324
throw new FileSystemException(null, null, "Symbolic link corrupted");
325
326
// copy into char array
327
char[] name = new char[nameLengthInBytes/2];
328
unsafe.copyMemory(null, buffer.address() + OFFSETOF_PATHBUFFER + nameOffset,
329
name, Unsafe.ARRAY_CHAR_BASE_OFFSET, nameLengthInBytes);
330
331
// remove special prefix
332
String target = stripPrefix(new String(name));
333
if (target.isEmpty()) {
334
throw new IOException("Symbolic link target is invalid");
335
}
336
return target;
337
} finally {
338
buffer.release();
339
}
340
}
341
342
/**
343
* Resolve all symbolic-links in a given absolute and normalized path
344
*/
345
private static WindowsPath resolveAllLinks(WindowsPath path)
346
throws IOException
347
{
348
assert path.isAbsolute();
349
WindowsFileSystem fs = path.getFileSystem();
350
351
// iterate through each name element of the path, resolving links as
352
// we go.
353
int linkCount = 0;
354
int elem = 0;
355
while (elem < path.getNameCount()) {
356
WindowsPath current = path.getRoot().resolve(path.subpath(0, elem+1));
357
358
WindowsFileAttributes attrs = null;
359
try {
360
attrs = WindowsFileAttributes.get(current, false);
361
} catch (WindowsException x) {
362
x.rethrowAsIOException(current);
363
}
364
365
/**
366
* If a symbolic link then we resolve it against the parent
367
* of the current name element. We then resolve any remaining
368
* part of the path against the result. The target of the link
369
* may have "." and ".." components so re-normalize and restart
370
* the process from the first element.
371
*/
372
if (attrs.isSymbolicLink()) {
373
linkCount++;
374
if (linkCount > 32)
375
throw new IOException("Too many links");
376
WindowsPath target = WindowsPath
377
.createFromNormalizedPath(fs, readLink(current));
378
WindowsPath remainder = null;
379
int count = path.getNameCount();
380
if ((elem+1) < count) {
381
remainder = path.subpath(elem+1, count);
382
}
383
path = current.getParent().resolve(target);
384
try {
385
String full = GetFullPathName(path.toString());
386
if (!full.equals(path.toString())) {
387
path = WindowsPath.createFromNormalizedPath(fs, full);
388
}
389
} catch (WindowsException x) {
390
x.rethrowAsIOException(path);
391
}
392
if (remainder != null) {
393
path = path.resolve(remainder);
394
}
395
396
// reset
397
elem = 0;
398
} else {
399
// not a link
400
elem++;
401
}
402
}
403
404
return path;
405
}
406
407
/**
408
* Strip long path or symbolic link prefix from path
409
*/
410
private static String stripPrefix(String path) {
411
// prefix for resolved/long path
412
if (path.startsWith("\\\\?\\")) {
413
if (path.startsWith("\\\\?\\UNC\\")) {
414
path = "\\" + path.substring(7);
415
} else {
416
path = path.substring(4);
417
}
418
return path;
419
}
420
421
// prefix for target of symbolic link
422
if (path.startsWith("\\??\\")) {
423
if (path.startsWith("\\??\\UNC\\")) {
424
path = "\\" + path.substring(7);
425
} else {
426
path = path.substring(4);
427
}
428
return path;
429
}
430
return path;
431
}
432
}
433
434