1use crate::file::FileOptions;
8use fidl_fuchsia_io as fio;
9use zx_status::Status;
10
11pub fn new_connection_validate_options(
16 options: &FileOptions,
17 readable: bool,
18 writable: bool,
19 executable: bool,
20) -> Result<(), Status> {
21 debug_assert!(!(writable && executable));
23
24 if !readable && options.rights.contains(fio::Operations::READ_BYTES) {
26 return Err(Status::ACCESS_DENIED);
27 }
28 if !writable && options.rights.contains(fio::Operations::WRITE_BYTES) {
29 return Err(Status::ACCESS_DENIED);
30 }
31 if !executable && options.rights.contains(fio::Operations::EXECUTE) {
32 return Err(Status::ACCESS_DENIED);
33 }
34
35 Ok(())
36}
37
38#[cfg(target_os = "fuchsia")]
40pub fn vmo_flags_to_rights(vmo_flags: fio::VmoFlags) -> fidl::Rights {
41 let mut rights = fidl::Rights::NONE;
43 if vmo_flags.contains(fio::VmoFlags::READ) {
44 rights |= fidl::Rights::READ;
45 }
46 if vmo_flags.contains(fio::VmoFlags::WRITE) {
47 rights |= fidl::Rights::WRITE;
48 }
49 if vmo_flags.contains(fio::VmoFlags::EXECUTE) {
50 rights |= fidl::Rights::EXECUTE;
51 }
52
53 rights
54}
55
56#[cfg(target_os = "fuchsia")]
61pub fn get_backing_memory_validate_flags(
62 vmo_flags: fio::VmoFlags,
63 connection_flags: fio::OpenFlags,
64) -> Result<(), Status> {
65 if vmo_flags.contains(fio::VmoFlags::PRIVATE_CLONE)
67 && vmo_flags.contains(fio::VmoFlags::SHARED_BUFFER)
68 {
69 return Err(Status::INVALID_ARGS);
70 }
71
72 if vmo_flags.contains(fio::VmoFlags::READ)
74 && !connection_flags.intersects(fio::OpenFlags::RIGHT_READABLE)
75 {
76 return Err(Status::ACCESS_DENIED);
77 }
78 if vmo_flags.contains(fio::VmoFlags::WRITE)
79 && !connection_flags.intersects(fio::OpenFlags::RIGHT_WRITABLE)
80 {
81 return Err(Status::ACCESS_DENIED);
82 }
83 if vmo_flags.contains(fio::VmoFlags::EXECUTE)
84 && !connection_flags.intersects(fio::OpenFlags::RIGHT_EXECUTABLE)
85 {
86 return Err(Status::ACCESS_DENIED);
87 }
88
89 if vmo_flags.contains(fio::VmoFlags::EXECUTE)
92 && !connection_flags.intersects(fio::OpenFlags::RIGHT_READABLE)
93 {
94 return Err(Status::ACCESS_DENIED);
95 }
96
97 Ok(())
98}
99
100#[cfg(test)]
101mod tests {
102 use super::new_connection_validate_options;
103 use crate::file::FileOptions;
104 use crate::protocols::ToFileOptions;
105 use crate::test_utils::build_flag_combinations;
106
107 use assert_matches::assert_matches;
108 use fidl_fuchsia_io as fio;
109 use zx_status::Status;
110
111 fn io_flags_to_rights(flags: fio::OpenFlags) -> (bool, bool, bool) {
112 return (
113 flags.intersects(fio::OpenFlags::RIGHT_READABLE),
114 flags.intersects(fio::OpenFlags::RIGHT_WRITABLE),
115 flags.intersects(fio::OpenFlags::RIGHT_EXECUTABLE),
116 );
117 }
118
119 fn ncvf(
120 flags: fio::OpenFlags,
121 readable: bool,
122 writable: bool,
123 executable: bool,
124 ) -> Result<FileOptions, Status> {
125 let options = flags.to_file_options()?;
126 new_connection_validate_options(&options, readable, writable, executable)?;
127 Ok(options)
128 }
129
130 #[test]
131 fn new_connection_validate_flags_posix() {
132 const ALL_POSIX_FLAGS: fio::OpenFlags = fio::OpenFlags::empty()
134 .union(fio::OpenFlags::POSIX_WRITABLE)
135 .union(fio::OpenFlags::POSIX_EXECUTABLE);
136 for open_flags in build_flag_combinations(
137 fio::OpenFlags::empty(),
138 fio::OpenFlags::RIGHT_READABLE
139 | fio::OpenFlags::RIGHT_WRITABLE
140 | fio::OpenFlags::RIGHT_EXECUTABLE
141 | ALL_POSIX_FLAGS,
142 ) {
143 let (readable, writable, executable) = io_flags_to_rights(open_flags);
144 if (writable && executable) || !open_flags.intersects(ALL_POSIX_FLAGS) {
146 continue;
147 }
148 assert_matches!(ncvf(open_flags, readable, writable, executable), Ok(_));
149 }
150 }
151
152 #[test]
153 fn new_connection_validate_flags_create() {
154 for open_flags in build_flag_combinations(
155 fio::OpenFlags::CREATE,
156 fio::OpenFlags::RIGHT_READABLE
157 | fio::OpenFlags::RIGHT_WRITABLE
158 | fio::OpenFlags::CREATE_IF_ABSENT,
159 ) {
160 let (readable, writable, executable) = io_flags_to_rights(open_flags);
161 assert_matches!(ncvf(open_flags, readable, writable, executable), Ok(_));
162 }
163 }
164
165 #[test]
166 fn new_connection_validate_flags_truncate() {
167 assert_matches!(
168 ncvf(fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::TRUNCATE, true, true, false,),
169 Err(Status::INVALID_ARGS)
170 );
171 assert_matches!(
172 ncvf(fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::TRUNCATE, true, true, false),
173 Ok(_)
174 );
175 assert_matches!(
176 ncvf(fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::TRUNCATE, true, false, false),
177 Err(Status::INVALID_ARGS)
178 );
179 }
180
181 #[test]
182 fn new_connection_validate_flags_append() {
183 assert_matches!(
184 ncvf(fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::APPEND, true, false, false),
185 Ok(_)
186 );
187 assert_matches!(
188 ncvf(fio::OpenFlags::RIGHT_READABLE | fio::OpenFlags::APPEND, true, true, false),
189 Ok(_)
190 );
191 assert_matches!(
192 ncvf(fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::APPEND, true, true, false),
193 Ok(_)
194 );
195 }
196
197 #[test]
198 fn new_connection_validate_flags_open_rights() {
199 for open_flags in build_flag_combinations(
200 fio::OpenFlags::empty(),
201 fio::OpenFlags::RIGHT_READABLE
202 | fio::OpenFlags::RIGHT_WRITABLE
203 | fio::OpenFlags::RIGHT_EXECUTABLE,
204 ) {
205 let (readable, writable, executable) = io_flags_to_rights(open_flags);
206
207 if !(writable && executable) {
210 assert_matches!(ncvf(open_flags, readable, writable, executable), Ok(_));
211 }
212
213 if readable && !(writable && executable) {
215 assert_eq!(
216 ncvf(open_flags, false, writable, executable),
217 Err(Status::ACCESS_DENIED)
218 );
219 }
220 if writable {
221 assert_eq!(
222 ncvf(open_flags, readable, false, executable),
223 Err(Status::ACCESS_DENIED)
224 );
225 }
226 if executable {
227 assert_eq!(ncvf(open_flags, readable, writable, false), Err(Status::ACCESS_DENIED));
228 }
229 }
230 }
231
232 #[cfg(target_os = "fuchsia")]
233 mod vmo_tests {
234 use super::super::{get_backing_memory_validate_flags, vmo_flags_to_rights};
235 use super::*;
236
237 fn rights_to_vmo_flags(readable: bool, writable: bool, executable: bool) -> fio::VmoFlags {
238 return if readable { fio::VmoFlags::READ } else { fio::VmoFlags::empty() }
239 | if writable { fio::VmoFlags::WRITE } else { fio::VmoFlags::empty() }
240 | if executable { fio::VmoFlags::EXECUTE } else { fio::VmoFlags::empty() };
241 }
242
243 #[test]
245 fn test_vmo_flags_to_rights() {
246 for vmo_flags in build_flag_combinations(
247 fio::VmoFlags::empty(),
248 fio::VmoFlags::READ | fio::VmoFlags::WRITE | fio::VmoFlags::EXECUTE,
249 ) {
250 let rights: fidl::Rights = vmo_flags_to_rights(vmo_flags);
251 assert_eq!(
252 vmo_flags.contains(fio::VmoFlags::READ),
253 rights.contains(fidl::Rights::READ)
254 );
255 assert_eq!(
256 vmo_flags.contains(fio::VmoFlags::WRITE),
257 rights.contains(fidl::Rights::WRITE)
258 );
259 assert_eq!(
260 vmo_flags.contains(fio::VmoFlags::EXECUTE),
261 rights.contains(fidl::Rights::EXECUTE)
262 );
263 }
264 }
265
266 #[test]
267 fn get_backing_memory_validate_flags_invalid() {
268 assert_eq!(
270 get_backing_memory_validate_flags(
271 fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::SHARED_BUFFER,
272 fio::OpenFlags::empty()
273 ),
274 Err(Status::INVALID_ARGS)
275 );
276 }
277
278 #[test]
281 fn get_backing_memory_validate_flags_less_rights() {
282 for open_flags in build_flag_combinations(
283 fio::OpenFlags::empty(),
284 fio::OpenFlags::RIGHT_READABLE
285 | fio::OpenFlags::RIGHT_WRITABLE
286 | fio::OpenFlags::RIGHT_EXECUTABLE,
287 ) {
288 let (readable, writable, executable) = io_flags_to_rights(open_flags);
289 let vmo_flags = rights_to_vmo_flags(readable, writable, executable);
290
291 if executable && !readable {
294 assert_eq!(
295 get_backing_memory_validate_flags(vmo_flags, open_flags),
296 Err(Status::ACCESS_DENIED)
297 );
298 continue;
299 }
300
301 get_backing_memory_validate_flags(vmo_flags, open_flags)
303 .expect("Failed to validate flags");
304
305 if readable {
307 let vmo_flags = rights_to_vmo_flags(false, writable, false);
308 get_backing_memory_validate_flags(vmo_flags, open_flags)
309 .expect("Failed to validate flags");
310 }
311 if writable {
312 let vmo_flags = rights_to_vmo_flags(readable, false, executable);
313 get_backing_memory_validate_flags(vmo_flags, open_flags)
314 .expect("Failed to validate flags");
315 }
316 if executable {
317 let vmo_flags = rights_to_vmo_flags(true, writable, false);
318 get_backing_memory_validate_flags(vmo_flags, open_flags)
319 .expect("Failed to validate flags");
320 }
321 }
322 }
323
324 #[test]
326 fn get_backing_memory_validate_flags_more_rights() {
327 for open_flags in build_flag_combinations(
328 fio::OpenFlags::empty(),
329 fio::OpenFlags::RIGHT_READABLE
330 | fio::OpenFlags::RIGHT_WRITABLE
331 | fio::OpenFlags::RIGHT_EXECUTABLE,
332 ) {
333 let (readable, writable, executable) = io_flags_to_rights(open_flags);
335 if !readable {
336 let vmo_flags = rights_to_vmo_flags(true, writable, executable);
337 assert_eq!(
338 get_backing_memory_validate_flags(vmo_flags, open_flags),
339 Err(Status::ACCESS_DENIED)
340 );
341 }
342 if !writable {
343 let vmo_flags = rights_to_vmo_flags(readable, true, false);
344 assert_eq!(
345 get_backing_memory_validate_flags(vmo_flags, open_flags),
346 Err(Status::ACCESS_DENIED)
347 );
348 }
349 if !executable {
350 let vmo_flags = rights_to_vmo_flags(readable, false, true);
351 assert_eq!(
352 get_backing_memory_validate_flags(vmo_flags, open_flags),
353 Err(Status::ACCESS_DENIED)
354 );
355 }
356 }
357 }
358 }
359}