]>
Commit | Line | Data |
---|---|---|
f12f8ff1 DM |
1 | use failure::*; |
2 | use nix::unistd; | |
3 | use nix::sys::stat; | |
4 | ||
5 | use std::fs::File; | |
6 | use std::io::Write; | |
7 | use std::path::Path; | |
8 | ||
9 | pub fn file_set_contents<P: AsRef<Path>>( | |
10 | path: P, | |
11 | data: &[u8], | |
12 | perm: Option<stat::Mode>, | |
13 | ) -> Result<(), Error> { | |
14 | ||
15 | let path = path.as_ref(); | |
16 | ||
d64d80d2 DM |
17 | // Note: we use mkstemp heŕe, because this worka with different |
18 | // processes, threads, and even tokio tasks. | |
f12f8ff1 DM |
19 | let mut template = path.to_owned(); |
20 | template.set_extension("tmp_XXXXXX"); | |
21 | let (fd, tmp_path) = match unistd::mkstemp(&template) { | |
22 | Ok((fd, path)) => (fd, path), | |
23 | Err(err) => bail!("mkstemp {:?} failed: {}", template, err), | |
24 | }; | |
25 | ||
26 | let tmp_path = tmp_path.as_path(); | |
27 | ||
1a7bc3dd | 28 | let mode : stat::Mode = perm.unwrap_or(stat::Mode::from( |
f12f8ff1 DM |
29 | stat::Mode::S_IRUSR | stat::Mode::S_IWUSR | |
30 | stat::Mode::S_IRGRP | stat::Mode::S_IROTH | |
1a7bc3dd | 31 | )); |
f12f8ff1 DM |
32 | |
33 | if let Err(err) = stat::fchmod(fd, mode) { | |
34 | let _ = unistd::unlink(tmp_path); | |
35 | bail!("fchmod {:?} failed: {}", tmp_path, err); | |
36 | } | |
37 | ||
38 | use std::os::unix::io::FromRawFd; | |
39 | let mut file = unsafe { File::from_raw_fd(fd) }; | |
40 | ||
41 | if let Err(err) = file.write_all(data) { | |
42 | let _ = unistd::unlink(tmp_path); | |
43 | bail!("write failed: {}", err); | |
44 | } | |
45 | ||
46 | if let Err(err) = std::fs::rename(tmp_path, path) { | |
47 | let _ = unistd::unlink(tmp_path); | |
48 | bail!("Atomic rename failed for file {:?} - {}", path, err); | |
49 | } | |
50 | ||
51 | Ok(()) | |
52 | } |