Line data Source code
1 : //! Enum-dispatch to the `OpenOptions` type of the respective [`super::IoEngineKind`];
2 :
3 : use std::os::fd::OwnedFd;
4 : use std::os::unix::fs::OpenOptionsExt;
5 : use std::path::Path;
6 :
7 : use super::io_engine::IoEngine;
8 :
9 : #[derive(Debug, Clone)]
10 : pub struct OpenOptions {
11 : /// We keep a copy of the write() flag we pass to the `inner`` `OptionOptions`
12 : /// to support [`Self::is_write`].
13 : write: bool,
14 : /// We don't expose + pass through a raw `custom_flags()` style API.
15 : /// The only custom flag we support is `O_DIRECT`, which we track here
16 : /// and map to `custom_flags()` in the [`Self::open`] method.
17 : direct: bool,
18 : inner: Inner,
19 : }
20 : #[derive(Debug, Clone)]
21 : enum Inner {
22 : StdFs(std::fs::OpenOptions),
23 : #[cfg(target_os = "linux")]
24 : TokioEpollUring(tokio_epoll_uring::ops::open_at::OpenOptions),
25 : }
26 :
27 : impl Default for OpenOptions {
28 3170 : fn default() -> Self {
29 3170 : let inner = match super::io_engine::get() {
30 0 : IoEngine::NotSet => panic!("io engine not set"),
31 0 : IoEngine::StdFs => Inner::StdFs(std::fs::OpenOptions::new()),
32 : #[cfg(target_os = "linux")]
33 : IoEngine::TokioEpollUring => {
34 3170 : Inner::TokioEpollUring(tokio_epoll_uring::ops::open_at::OpenOptions::new())
35 : }
36 : };
37 3170 : Self {
38 3170 : write: false,
39 3170 : direct: false,
40 3170 : inner,
41 3170 : }
42 3170 : }
43 : }
44 :
45 : impl OpenOptions {
46 3170 : pub fn new() -> OpenOptions {
47 3170 : Self::default()
48 3170 : }
49 :
50 2654 : pub(super) fn is_write(&self) -> bool {
51 2654 : self.write
52 2654 : }
53 :
54 298390 : pub(super) fn is_direct(&self) -> bool {
55 298390 : self.direct
56 298390 : }
57 :
58 2034 : pub fn read(mut self, read: bool) -> Self {
59 2034 : match &mut self.inner {
60 0 : Inner::StdFs(x) => {
61 0 : let _ = x.read(read);
62 0 : }
63 : #[cfg(target_os = "linux")]
64 2034 : Inner::TokioEpollUring(x) => {
65 2034 : let _ = x.read(read);
66 2034 : }
67 : }
68 2034 : self
69 2034 : }
70 :
71 1807 : pub fn write(mut self, write: bool) -> Self {
72 1807 : self.write = write;
73 1807 : match &mut self.inner {
74 0 : Inner::StdFs(x) => {
75 0 : let _ = x.write(write);
76 0 : }
77 : #[cfg(target_os = "linux")]
78 1807 : Inner::TokioEpollUring(x) => {
79 1807 : let _ = x.write(write);
80 1807 : }
81 : }
82 1807 : self
83 1807 : }
84 :
85 3172 : pub fn create(mut self, create: bool) -> Self {
86 3172 : match &mut self.inner {
87 0 : Inner::StdFs(x) => {
88 0 : let _ = x.create(create);
89 0 : }
90 : #[cfg(target_os = "linux")]
91 3172 : Inner::TokioEpollUring(x) => {
92 3172 : let _ = x.create(create);
93 3172 : }
94 : }
95 3172 : self
96 3172 : }
97 :
98 4975 : pub fn create_new(mut self, create_new: bool) -> Self {
99 4975 : match &mut self.inner {
100 0 : Inner::StdFs(x) => {
101 0 : let _ = x.create_new(create_new);
102 0 : }
103 : #[cfg(target_os = "linux")]
104 4975 : Inner::TokioEpollUring(x) => {
105 4975 : let _ = x.create_new(create_new);
106 4975 : }
107 : }
108 4975 : self
109 4975 : }
110 :
111 3172 : pub fn truncate(mut self, truncate: bool) -> Self {
112 3172 : match &mut self.inner {
113 0 : Inner::StdFs(x) => {
114 0 : let _ = x.truncate(truncate);
115 0 : }
116 : #[cfg(target_os = "linux")]
117 3172 : Inner::TokioEpollUring(x) => {
118 3172 : let _ = x.truncate(truncate);
119 3172 : }
120 : }
121 3172 : self
122 3172 : }
123 :
124 : /// Don't use, `O_APPEND` is not supported.
125 0 : pub fn append(&mut self, _append: bool) {
126 0 : super::io_engine::panic_operation_must_be_idempotent();
127 0 : }
128 :
129 99022 : pub(in crate::virtual_file) async fn open(&self, path: &Path) -> std::io::Result<OwnedFd> {
130 : #[cfg_attr(not(target_os = "linux"), allow(unused_mut))]
131 99022 : let mut custom_flags = 0;
132 99022 : if self.direct {
133 : #[cfg(target_os = "linux")]
134 98506 : {
135 98506 : custom_flags |= nix::libc::O_DIRECT;
136 98506 : }
137 : #[cfg(not(target_os = "linux"))]
138 : {
139 : // Other platforms may be used for development but don't necessarily have a 1:1 equivalent to Linux's O_DIRECT (macOS!).
140 : // Just don't set the flag; to catch alignment bugs typical for O_DIRECT,
141 : // we have a runtime validation layer inside `VirtualFile::write_at` and `VirtualFile::read_at`.
142 : static WARNING: std::sync::Once = std::sync::Once::new();
143 : WARNING.call_once(|| {
144 : let span = tracing::info_span!(parent: None, "open_options");
145 : let _enter = span.enter();
146 : tracing::warn!("your platform is not a supported production platform, ignoing request for O_DIRECT; this could hide alignment bugs; this warning is logged once per process");
147 : });
148 : }
149 516 : }
150 :
151 99022 : match self.inner.clone() {
152 0 : Inner::StdFs(mut x) => x
153 0 : .custom_flags(custom_flags)
154 0 : .open(path)
155 0 : .map(|file| file.into()),
156 : #[cfg(target_os = "linux")]
157 99022 : Inner::TokioEpollUring(mut x) => {
158 99022 : x.custom_flags(custom_flags);
159 99022 : let system = super::io_engine::tokio_epoll_uring_ext::thread_local_system().await;
160 99022 : let (_, res) = super::io_engine::retry_ecanceled_once((), |()| async {
161 99022 : let res = system.open(path, &x).await;
162 99022 : ((), res)
163 198044 : })
164 99022 : .await;
165 99022 : res.map_err(super::io_engine::epoll_uring_error_to_std)
166 : }
167 : }
168 99022 : }
169 :
170 0 : pub fn mode(mut self, mode: u32) -> Self {
171 0 : match &mut self.inner {
172 0 : Inner::StdFs(x) => {
173 0 : let _ = x.mode(mode);
174 0 : }
175 : #[cfg(target_os = "linux")]
176 0 : Inner::TokioEpollUring(x) => {
177 0 : let _ = x.mode(mode);
178 0 : }
179 : }
180 0 : self
181 0 : }
182 :
183 2654 : pub fn direct(mut self, direct: bool) -> Self {
184 2654 : self.direct = direct;
185 2654 : self
186 2654 : }
187 : }
|