mirror of
https://github.com/denoland/std.git
synced 2024-11-21 20:50:22 +00:00
fix(fs/ensure_symlink): check symlink is pointing the given target (#4371)
This commit is contained in:
parent
ef6b95f0c7
commit
1e0764dc23
@ -19,6 +19,8 @@ function resolveSymlinkTarget(target: string | URL, linkName: string | URL) {
|
||||
/**
|
||||
* Ensures that the link exists, and points to a valid file.
|
||||
* If the directory structure does not exist, it is created.
|
||||
* If the link already exists, it is not modified but error is thrown if it is not point to the given target.
|
||||
* Requires the `--allow-read` and `--allow-write` flag.
|
||||
*
|
||||
* @param target the source file path
|
||||
* @param linkName the destination link path
|
||||
@ -45,12 +47,28 @@ export async function ensureSymlink(
|
||||
if (!(error instanceof Deno.errors.AlreadyExists)) {
|
||||
throw error;
|
||||
}
|
||||
const linkStatInfo = await Deno.lstat(linkName);
|
||||
if (!linkStatInfo.isSymlink) {
|
||||
const type = getFileInfoType(linkStatInfo);
|
||||
throw new Deno.errors.AlreadyExists(
|
||||
`A '${type}' already exists at the path: ${linkName}`,
|
||||
);
|
||||
}
|
||||
const linkPath = await Deno.readLink(linkName);
|
||||
const linkRealPath = resolve(linkPath);
|
||||
if (linkRealPath !== targetRealPath) {
|
||||
throw new Deno.errors.AlreadyExists(
|
||||
`A symlink targeting to an undesired path already exists: ${linkName} -> ${linkRealPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures that the link exists, and points to a valid file.
|
||||
* If the directory structure does not exist, it is created.
|
||||
* If the link already exists, it is not modified but error is thrown if it is not point to the given target.
|
||||
* Requires the `--allow-read` and `--allow-write` flag.
|
||||
*
|
||||
* @param target the source file path
|
||||
* @param linkName the destination link path
|
||||
@ -77,5 +95,19 @@ export function ensureSymlinkSync(
|
||||
if (!(error instanceof Deno.errors.AlreadyExists)) {
|
||||
throw error;
|
||||
}
|
||||
const linkStatInfo = Deno.lstatSync(linkName);
|
||||
if (!linkStatInfo.isSymlink) {
|
||||
const type = getFileInfoType(linkStatInfo);
|
||||
throw new Deno.errors.AlreadyExists(
|
||||
`A '${type}' already exists at the path: ${linkName}`,
|
||||
);
|
||||
}
|
||||
const linkPath = Deno.readLinkSync(linkName);
|
||||
const linkRealPath = resolve(linkPath);
|
||||
if (linkRealPath !== targetRealPath) {
|
||||
throw new Deno.errors.AlreadyExists(
|
||||
`A symlink targeting to an undesired path already exists: ${linkName} -> ${linkRealPath}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,6 +80,74 @@ Deno.test("ensureSymlinkSync() ensures linkName links to target", function () {
|
||||
Deno.removeSync(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
Deno.test("ensureSymlink() rejects if the linkName path already exist", async function () {
|
||||
const testDir = path.join(testdataDir, "link_file_5");
|
||||
const linkFile = path.join(testDir, "test.txt");
|
||||
const linkDir = path.join(testDir, "test_dir");
|
||||
const linkSymlink = path.join(testDir, "test_symlink");
|
||||
const targetFile = path.join(testDir, "target.txt");
|
||||
|
||||
await Deno.mkdir(testDir, { recursive: true });
|
||||
await Deno.writeTextFile(linkFile, "linkFile");
|
||||
await Deno.mkdir(linkDir);
|
||||
await Deno.symlink("non-existent", linkSymlink, { type: "file" });
|
||||
await Deno.writeTextFile(targetFile, "targetFile");
|
||||
|
||||
await assertRejects(
|
||||
async () => {
|
||||
await ensureSymlink(targetFile, linkFile);
|
||||
},
|
||||
);
|
||||
await assertRejects(
|
||||
async () => {
|
||||
await ensureSymlink(targetFile, linkDir);
|
||||
},
|
||||
);
|
||||
await assertRejects(
|
||||
async () => {
|
||||
await ensureSymlink(targetFile, linkSymlink);
|
||||
},
|
||||
);
|
||||
|
||||
assertEquals(await Deno.readTextFile(linkFile), "linkFile");
|
||||
assertEquals((await Deno.stat(linkDir)).isDirectory, true);
|
||||
assertEquals(await Deno.readLink(linkSymlink), "non-existent");
|
||||
assertEquals(await Deno.readTextFile(targetFile), "targetFile");
|
||||
|
||||
await Deno.remove(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
Deno.test("ensureSymlinkSync() throws if the linkName path already exist", function () {
|
||||
const testDir = path.join(testdataDir, "link_file_6");
|
||||
const linkFile = path.join(testDir, "test.txt");
|
||||
const linkDir = path.join(testDir, "test_dir");
|
||||
const linkSymlink = path.join(testDir, "test_symlink");
|
||||
const targetFile = path.join(testDir, "target.txt");
|
||||
|
||||
Deno.mkdirSync(testDir, { recursive: true });
|
||||
Deno.writeTextFileSync(linkFile, "linkFile");
|
||||
Deno.mkdirSync(linkDir);
|
||||
Deno.symlinkSync("non-existent", linkSymlink, { type: "file" });
|
||||
Deno.writeTextFileSync(targetFile, "targetFile");
|
||||
|
||||
assertThrows(() => {
|
||||
ensureSymlinkSync(targetFile, linkFile);
|
||||
});
|
||||
assertThrows(() => {
|
||||
ensureSymlinkSync(targetFile, linkDir);
|
||||
});
|
||||
assertThrows(() => {
|
||||
ensureSymlinkSync(targetFile, linkSymlink);
|
||||
});
|
||||
|
||||
assertEquals(Deno.readTextFileSync(linkFile), "linkFile");
|
||||
assertEquals(Deno.statSync(linkDir).isDirectory, true);
|
||||
assertEquals(Deno.readLinkSync(linkSymlink), "non-existent");
|
||||
assertEquals(Deno.readTextFileSync(targetFile), "targetFile");
|
||||
|
||||
Deno.removeSync(testDir, { recursive: true });
|
||||
});
|
||||
|
||||
Deno.test("ensureSymlink() ensures dir linkName links to dir target", async function () {
|
||||
const testDir = path.join(testdataDir, "link_file_origin_3");
|
||||
const linkDir = path.join(testdataDir, "link_file_link_3");
|
||||
|
Loading…
Reference in New Issue
Block a user