-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Type of a Link created on Windows can be changed #53684
Comments
I think, only from reading the linked doc, that a Windows symbolic link inherently knows whether it points to a file or directory, and the symbolic link won't change that even if the target is removed or replaced by something else. I have no idea what the effect is of following a file link to a directory, or vice versa. Which is (probably) why the (woefully under-documented) The link doesn't change, but you can change it explicitly, which probably amounts to overwriting it with a new symbolic link. |
Let's try to create a link pointing to a directory, then delete the target and create a file with the same name/path and to see the link's type import "dart:io";
main() {
Directory sandbox = Directory.systemTemp.createTempSync();
try {
Directory target1 = sandbox.createTempSync();
String path = target1.path;
// Create link to a Directory
Link link = new Link(sandbox.path + Platform.pathSeparator + "link.tmp");
link.createSync(target1.path);
print(FileSystemEntity.typeSync(link.path)); // Windows: directory, Linux: directory
print(FileSystemEntity.typeSync(link.path, followLinks: false)); // Windows:link , Linux: link
// Delete those Directory and create a File with the same path
target1.deleteSync();
print(target1.existsSync()); // false
print(FileSystemEntity.typeSync(link.path)); // Windows: link, Linux: notFound
print(FileSystemEntity.typeSync(link.path, followLinks: false)); // Windows: link, Linux: link
File target2 = File(path);
target2.createSync();
print(target2.existsSync()); // true
print(FileSystemEntity.typeSync(link.path)); // Windows: link, Linux: file
print(FileSystemEntity.typeSync(link.path, followLinks: false)); // Windows: link, Linux: link
} finally {
sandbox.deleteSync(recursive: true);
}
} I'd say that Linux behaves exactly like I'd intuitive expect. As for Windows it seems that actual behaviour contradicts at least two documentation statement
Even if it's not a bug but expected behavior we should have more detailed documentation |
And one more Windows (documentation?) issue.
On Windows type of the link, pointing to a not existing entity is import "dart:io";
main() {
Directory sandbox = Directory.systemTemp.createTempSync();
try {
Directory target1 = sandbox.createTempSync();
target1.deleteSync();
Link link = new Link(sandbox.path + Platform.pathSeparator + "link.tmp");
link.createSync(target1.path);
print(FileSystemEntity.typeSync(link.path)); // Windows: link, Linux: notFound
print(FileSystemEntity.typeSync(link.path, followLinks: false)); // Windows: link, Linux: link
} finally {
sandbox.deleteSync(recursive: true);
}
} |
That can still be a file link with no target. The behavior seen her it's consistent with a file or directory link with no valid target reporting itself as a link. What happens if one creates either a file or directory at the target of the link? I guess, somewhat blindly, that it will start reporting itself as a file link if it points to a file, and still as a link if it points to a director, because it's a file link. Could be. Would be consistent with the documentation. The documentation could say more, though. It's easy to be consistent with something vague. |
It's not possible to create "file/directory" only link. Link target is a So, if you create a link pointing to nowhere, then, on Linux it'll have type import "dart:io";
main() {
Directory sandbox = Directory.systemTemp.createTempSync();
try {
String notExisting = "something";
Link link = Link(sandbox.path + Platform.pathSeparator + "link");
String path = sandbox.path + Platform.pathSeparator + notExisting;
link.createSync(path);
print(FileSystemEntity.typeSync(link.path)); // Linux: notFound, Windows: link
File file = File(path);
file.createSync();
print(FileSystemEntity.typeSync(link.path)); // Linux: file, Windows: file
file.deleteSync();
print(FileSystemEntity.typeSync(link.path)); // Linux: notFound, Windows: link
Directory dir = Directory(path);
dir.createSync();
print(FileSystemEntity.typeSync(link.path)); // Linux: directory, Windows: link
} finally {
sandbox.deleteSync(recursive: true);
}
} |
That still looks consistent.
That's apparently not true on Windows. Every link you create is either a file link or a directory link. A file link reports its transitive ( Symmetrically, a directory link reports its transitive type as That matches the prints in code above:
Try this: import "dart:io";
void main() async {
var tmp = Directory.systemTemp.createTempSync("winlink");
try {
var file = File(join(tmp.path, "file"));
file.writeAsStringSync("banana");
var dir = Directory(join(tmp.path, "dir"));
dir.createSync();
var fileLink = Link(join(tmp.path, "file-link"));
fileLink.createSync(file.path);
var dirLink = Link(join(tmp.path, "dir-link"));
dirLink.createSync(dir.path);
// File link, file target: type file
log(fileLink); // ...\file-link:link--> ...\file:file (expected file)
// Dir link, dir target: type dir
log(dirLink); // ...\dir-link:link--> ...\dir:directory (expected directory)
await (Future.delayed(Duration(milliseconds: 250)));
file.deleteSync();
dir.deleteSync(recursive: true);
{
var newFile = File(dir.path);
newFile.createSync();
var newDir = Directory(file.path);
newDir.createSync();
// File link, dir target: type link, and cannot resolve
log(fileLink); // ...\file-link:link--> no target:notFound (expected link)
// Dir link, fil target: type link, and cannot resolve
log(dirLink); // ...\dir-link:link--> no target:notFound (expected link)
await (Future.delayed(Duration(milliseconds: 250)));
newFile.deleteSync();
newDir.deleteSync();
}
{
var newFile = File(dir.path);
newFile.createSync();
var newLink = Link(file.path);
newLink.createSync(newFile.path);
// File link, valid file link target: type file
log(fileLink); // ...\file-link:link--> ...\dir:file (expected file)
// File link, file target: type file
log(newLink); // ...\file:link--> ...\dir:file (expected file)
await (Future.delayed(Duration(milliseconds: 250)));
newFile.deleteSync();
newLink.deleteSync();
}
} finally {
await (Future.delayed(Duration(milliseconds: 250)));
tmp.deleteSync(recursive: true);
}
}
String join(String dir, String name) =>
[dir, name].join(Platform.pathSeparator);
void log(Link fse) {
var exists = fse.existsSync();
assert(exists);
var transitiveType = FileSystemEntity.typeSync(fse.path, followLinks: true);
var directType = FileSystemEntity.typeSync(fse.path, followLinks: false);
//assert(directType == stat.type);
var target = "no target";
var targetType = FileSystemEntityType.notFound;
if (transitiveType == FileSystemEntityType.file ||
transitiveType == FileSystemEntityType.directory) {
target = fse.resolveSymbolicLinksSync();
targetType = FileSystemEntity.isFileSync(target)
? FileSystemEntityType.file
: FileSystemEntity.isDirectorySync(target)
? FileSystemEntityType.directory
: FileSystemEntity.isLinkSync(target)
? FileSystemEntityType.link
: FileSystemEntityType.notFound;
}
print("${fse.path}:$directType"
"${directType == FileSystemEntityType.link ? //
"--> $target:$targetType" : ""}"
" (expected $transitiveType)");
}
I think the documentation is technically correct, it's just that where it uses the word "type" in this paragraph, it really means "Windows link supported target type", not |
@lrhn thank you for your assistance! Let me try to summarize the above.
2.1. 2.2.
Add that on Windows it returns
Add that on non-Windows systems it returns 2.3.
Need to rewrite this statement in more details. Link, created with a directory as a target can point to directories only (or orher links pointing to a directories). Link, created with a target file/nowhere/link to file/link to nowhere cannot point to a directory/link to directory 2.4. 2.5. Did I miss anything? |
Agree that docs need improvement. |
Sorry for jumping in late - I wrote the text "The link represents a file or directory and does not change its type after creation." The meaning of that is Windows represents links-to-files and links-to-directories differently. For example, to delete a link-to-file, you'd use I'll work on documentation fixes. |
Stuff to do:
|
I have a PR that fixes these issues (https://dart-review.googlesource.com/c/sdk/+/331841) except for the issues related to #53689, which I will fix separately. Instead of documenting the difference in testBrokenLinkTypeSync() {
String base = Directory.systemTemp.createTempSync('dart_link').path;
String link = join(base, 'link');
Link(link).createSync('does not exist');
Expect.equals(FileSystemEntityType.link,
FileSystemEntity.typeSync(link, followLinks: false));
Expect.equals(FileSystemEntityType.notFound,
FileSystemEntity.typeSync(link, followLinks: true));
} Several co19 tests must be updated before this PR can land without breakage: |
…resolving a link that points to a non-existent file results in a type of `notFound`, which is consistent with all other platforms. Bug:#53684 Change-Id: I1b594e1a85906d1f510358eec71792ea15ab801b CoreLibraryReviewExempt: VM- and doc-only Tested: unit tests Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/331841 Reviewed-by: Lasse Nielsen <lrn@google.com> Commit-Queue: Brian Quinlan <bquinlan@google.com>
According to the
Link.create()
documentationsdk/sdk/lib/io/link.dart
Lines 50 to 54 in 24ec82e
link created by
Link.create()
cannot change it's type. What exactlydoes not change its type after creation
mean? I understand it as Directory link cannot be turned into File link and vice versa. But it isn't so. Proof:cc @brianquinlan
Tested on
Dart SDK version: 3.2.0-220.0.dev (dev) (Sat Sep 30 13:02:52 2023 -0700) on "windows_x64"
onWindows 11 Pro
The text was updated successfully, but these errors were encountered: