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