1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
use bun_core::ZStr;
use crate::{E, ErrorCase, Fd, FdExt, O, Tag};
// O_TMPFILE doesn't seem to work very well.
const ALLOW_TMPFILE: bool = false;
// To be used with files
// not folders!
pub struct Tmpfile<'a> {
pub destination_dir: Fd,
// BORROW_PARAM (Tmpfile.zig:5): caller-supplied tmp name, valid for the
// lifetime of the Tmpfile.
pub tmpfilename: &'a ZStr,
pub fd: Fd,
pub using_tmpfile: bool,
}
impl<'a> Tmpfile<'a> {
pub fn create(destination_dir: Fd, tmpfilename: &'a ZStr) -> crate::Result<Tmpfile<'a>> {
let perm = 0o644;
let mut tmpfile = Tmpfile {
destination_dir,
tmpfilename,
fd: Fd::INVALID,
using_tmpfile: ALLOW_TMPFILE,
};
'open: {
// ALLOW_TMPFILE = false (Zig comment: O_TMPFILE doesn't seem to work
// very well). Dead in Zig too, but Zig comptime drops it; Rust still
// type-checks `if false` bodies, so the body must resolve.
if ALLOW_TMPFILE {
// SAFETY: literal is NUL-terminated; len excludes the NUL.
let dot = ZStr::from_static(b".\0");
match crate::openat(
destination_dir,
dot,
O::WRONLY | O::TMPFILE | O::CLOEXEC,
perm,
) {
Ok(fd) => {
tmpfile.fd =
fd.make_lib_uv_owned_for_syscall(Tag::open, ErrorCase::CloseOnFail)?;
break 'open;
}
// PORT NOTE: Zig matched .OPNOTSUPP; on Linux that aliases ENOTSUP.
Err(err) => match err.get_errno() {
E::EINVAL | E::ENOTSUP | E::ENOSYS => {
tmpfile.using_tmpfile = false;
}
_ => return Err(err),
},
}
}
tmpfile.fd = crate::openat(
destination_dir,
tmpfilename,
O::CREAT | O::EXCL | O::CLOEXEC | O::WRONLY,
perm,
)?
.make_lib_uv_owned_for_syscall(Tag::open, ErrorCase::CloseOnFail)?;
}
Ok(tmpfile)
}
// TODO(port): narrow error set
pub fn finish(&mut self, destname: &ZStr) -> Result<(), bun_core::Error> {
// ALLOW_TMPFILE = false dead branch — see `create()` note above.
if ALLOW_TMPFILE && self.using_tmpfile {
let mut retry = true;
// SAFETY: basename returns a suffix of `destname`, which is NUL-terminated,
// so the suffix is also NUL-terminated at the same position.
let basename: &ZStr = unsafe {
let b = bun_paths::basename(destname.as_bytes());
ZStr::from_raw(b.as_ptr(), b.len())
};
while retry {
match crate::linkat_tmpfile(self.fd, self.destination_dir, basename) {
Ok(()) => return Ok(()),
Err(err) if err.get_errno() == E::EEXIST && retry => {
let _ = crate::unlinkat(self.destination_dir, basename);
retry = false;
}
Err(err) => return Err(err.into()),
}
}
}
crate::move_file_z_with_handle(
self.fd,
self.destination_dir,
self.tmpfilename,
self.destination_dir,
destname,
)
}
}
// ported from: src/sys/tmp.zig