1 use crate::error::{Error, Result};
2 use crate::gen::fs;
3 use crate::paths;
4 use std::path::Path;
5
write(path: impl AsRef<Path>, content: &[u8]) -> Result<()>6 pub(crate) fn write(path: impl AsRef<Path>, content: &[u8]) -> Result<()> {
7 let path = path.as_ref();
8
9 let mut create_dir_error = None;
10 if path.exists() {
11 if let Ok(existing) = fs::read(path) {
12 if existing == content {
13 // Avoid bumping modified time with unchanged contents.
14 return Ok(());
15 }
16 }
17 best_effort_remove(path);
18 } else {
19 let parent = path.parent().unwrap();
20 create_dir_error = fs::create_dir_all(parent).err();
21 }
22
23 match fs::write(path, content) {
24 // As long as write succeeded, ignore any create_dir_all error.
25 Ok(()) => Ok(()),
26 // If create_dir_all and write both failed, prefer the first error.
27 Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
28 }
29 }
30
symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()>31 pub(crate) fn symlink_file(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
32 let original = original.as_ref();
33 let link = link.as_ref();
34
35 let mut create_dir_error = None;
36 if link.exists() {
37 best_effort_remove(link);
38 } else {
39 let parent = link.parent().unwrap();
40 create_dir_error = fs::create_dir_all(parent).err();
41 }
42
43 match paths::symlink_or_copy(original, link) {
44 // As long as symlink_or_copy succeeded, ignore any create_dir_all error.
45 Ok(()) => Ok(()),
46 // If create_dir_all and symlink_or_copy both failed, prefer the first error.
47 Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
48 }
49 }
50
symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()>51 pub(crate) fn symlink_dir(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<()> {
52 let original = original.as_ref();
53 let link = link.as_ref();
54
55 let mut create_dir_error = None;
56 if link.exists() {
57 best_effort_remove(link);
58 } else {
59 let parent = link.parent().unwrap();
60 create_dir_error = fs::create_dir_all(parent).err();
61 }
62
63 match fs::symlink_dir(original, link) {
64 // As long as symlink_dir succeeded, ignore any create_dir_all error.
65 Ok(()) => Ok(()),
66 // If create_dir_all and symlink_dir both failed, prefer the first error.
67 Err(err) => Err(Error::Fs(create_dir_error.unwrap_or(err))),
68 }
69 }
70
best_effort_remove(path: &Path)71 fn best_effort_remove(path: &Path) {
72 use std::fs;
73
74 let file_type = match if cfg!(windows) {
75 // On Windows, the correct choice of remove_file vs remove_dir needs to
76 // be used according to what the symlink *points to*. Trying to use
77 // remove_file to remove a symlink which points to a directory fails
78 // with "Access is denied".
79 fs::metadata(path)
80 } else {
81 // On non-Windows, we check metadata not following symlinks. All
82 // symlinks are removed using remove_file.
83 fs::symlink_metadata(path)
84 } {
85 Ok(metadata) => metadata.file_type(),
86 Err(_) => return,
87 };
88
89 if file_type.is_dir() {
90 let _ = fs::remove_dir_all(path);
91 } else {
92 let _ = fs::remove_file(path);
93 }
94 }
95