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 options: FileOptions,
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 && !options.rights.intersects(fio::Operations::READ_BYTES)
75 {
76 return Err(Status::ACCESS_DENIED);
77 }
78 if vmo_flags.contains(fio::VmoFlags::WRITE)
79 && !options.rights.intersects(fio::Operations::WRITE_BYTES)
80 {
81 return Err(Status::ACCESS_DENIED);
82 }
83 if vmo_flags.contains(fio::VmoFlags::EXECUTE)
84 && !options.rights.intersects(fio::Operations::EXECUTE)
85 {
86 return Err(Status::ACCESS_DENIED);
87 }
88
89 if vmo_flags.contains(fio::VmoFlags::EXECUTE)
92 && !options.rights.intersects(fio::Operations::READ_BYTES)
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 options_to_rights(options: FileOptions) -> (bool, bool, bool) {
112 return (
113 options.rights.intersects(fio::Operations::READ_BYTES),
114 options.rights.intersects(fio::Operations::WRITE_BYTES),
115 options.rights.intersects(fio::Operations::EXECUTE),
116 );
117 }
118
119 fn ncvf(
120 flags: impl ToFileOptions,
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_create() {
132 for open_flags in build_flag_combinations(
133 fio::Flags::FLAG_MAYBE_CREATE,
134 fio::Flags::PERM_READ_BYTES
135 | fio::Flags::PERM_WRITE_BYTES
136 | fio::Flags::FLAG_MUST_CREATE,
137 ) {
138 let options = open_flags.to_file_options().unwrap();
139 let (readable, writable, executable) = options_to_rights(options);
140 assert_matches!(ncvf(options, readable, writable, executable), Ok(_));
141 }
142 }
143
144 #[test]
145 fn new_connection_validate_flags_truncate() {
146 assert_matches!(
147 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_TRUNCATE, true, true, false),
148 Err(Status::INVALID_ARGS)
149 );
150 assert_matches!(
151 ncvf(fio::Flags::PERM_WRITE_BYTES | fio::Flags::FILE_TRUNCATE, true, true, false),
152 Ok(_)
153 );
154 assert_matches!(
155 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_TRUNCATE, true, false, false),
156 Err(Status::INVALID_ARGS)
157 );
158 }
159
160 #[test]
161 fn new_connection_validate_flags_append() {
162 assert_matches!(
163 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_APPEND, true, false, false),
164 Ok(_)
165 );
166 assert_matches!(
167 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_APPEND, true, true, false),
168 Ok(_)
169 );
170 assert_matches!(
171 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_APPEND, true, true, false),
172 Ok(_)
173 );
174 }
175
176 #[test]
177 fn new_connection_validate_flags_open_rights() {
178 for open_flags in build_flag_combinations(
179 fio::Flags::empty(),
180 fio::Flags::PERM_READ_BYTES | fio::Flags::PERM_READ_BYTES | fio::Flags::PERM_EXECUTE,
181 ) {
182 let options = open_flags.to_file_options().unwrap();
183 let (readable, writable, executable) = options_to_rights(options);
184
185 if !(writable && executable) {
188 assert_matches!(ncvf(options, readable, writable, executable), Ok(_));
189 }
190
191 if readable && !(writable && executable) {
193 assert_eq!(ncvf(options, false, writable, executable), Err(Status::ACCESS_DENIED));
194 }
195 if writable {
196 assert_eq!(ncvf(options, readable, false, executable), Err(Status::ACCESS_DENIED));
197 }
198 if executable {
199 assert_eq!(ncvf(options, readable, writable, false), Err(Status::ACCESS_DENIED));
200 }
201 }
202 }
203
204 #[cfg(target_os = "fuchsia")]
205 mod vmo_tests {
206 use super::super::{get_backing_memory_validate_flags, vmo_flags_to_rights};
207 use super::*;
208
209 fn rights_to_vmo_flags(readable: bool, writable: bool, executable: bool) -> fio::VmoFlags {
210 return if readable { fio::VmoFlags::READ } else { fio::VmoFlags::empty() }
211 | if writable { fio::VmoFlags::WRITE } else { fio::VmoFlags::empty() }
212 | if executable { fio::VmoFlags::EXECUTE } else { fio::VmoFlags::empty() };
213 }
214
215 #[test]
217 fn test_vmo_flags_to_rights() {
218 for vmo_flags in build_flag_combinations(
219 fio::VmoFlags::empty(),
220 fio::VmoFlags::READ | fio::VmoFlags::WRITE | fio::VmoFlags::EXECUTE,
221 ) {
222 let rights: fidl::Rights = vmo_flags_to_rights(vmo_flags);
223 assert_eq!(
224 vmo_flags.contains(fio::VmoFlags::READ),
225 rights.contains(fidl::Rights::READ)
226 );
227 assert_eq!(
228 vmo_flags.contains(fio::VmoFlags::WRITE),
229 rights.contains(fidl::Rights::WRITE)
230 );
231 assert_eq!(
232 vmo_flags.contains(fio::VmoFlags::EXECUTE),
233 rights.contains(fidl::Rights::EXECUTE)
234 );
235 }
236 }
237
238 #[test]
239 fn get_backing_memory_validate_flags_invalid() {
240 assert_eq!(
242 get_backing_memory_validate_flags(
243 fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::SHARED_BUFFER,
244 fio::Flags::empty().to_file_options().unwrap()
245 ),
246 Err(Status::INVALID_ARGS)
247 );
248 }
249
250 #[test]
253 fn get_backing_memory_validate_flags_less_rights() {
254 for open_flags in build_flag_combinations(
255 fio::Flags::empty(),
256 fio::Flags::PERM_READ_BYTES
257 | fio::Flags::PERM_WRITE_BYTES
258 | fio::Flags::PERM_EXECUTE,
259 ) {
260 let options = open_flags.to_file_options().unwrap();
261 let (readable, writable, executable) = options_to_rights(options);
262 let vmo_flags = rights_to_vmo_flags(readable, writable, executable);
263
264 if executable && !readable {
267 assert_eq!(
268 get_backing_memory_validate_flags(vmo_flags, options),
269 Err(Status::ACCESS_DENIED)
270 );
271 continue;
272 }
273
274 get_backing_memory_validate_flags(vmo_flags, options)
276 .expect("Failed to validate flags");
277
278 if readable {
280 let vmo_flags = rights_to_vmo_flags(false, writable, false);
281 get_backing_memory_validate_flags(vmo_flags, options)
282 .expect("Failed to validate flags");
283 }
284 if writable {
285 let vmo_flags = rights_to_vmo_flags(readable, false, executable);
286 get_backing_memory_validate_flags(vmo_flags, options)
287 .expect("Failed to validate flags");
288 }
289 if executable {
290 let vmo_flags = rights_to_vmo_flags(true, writable, false);
291 get_backing_memory_validate_flags(vmo_flags, options)
292 .expect("Failed to validate flags");
293 }
294 }
295 }
296
297 #[test]
299 fn get_backing_memory_validate_flags_more_rights() {
300 for open_flags in build_flag_combinations(
301 fio::Flags::empty(),
302 fio::Flags::PERM_READ_BYTES
303 | fio::Flags::PERM_WRITE_BYTES
304 | fio::Flags::PERM_EXECUTE,
305 ) {
306 let options = open_flags.to_file_options().unwrap();
307 let (readable, writable, executable) = options_to_rights(options);
309 if !readable {
310 let vmo_flags = rights_to_vmo_flags(true, writable, executable);
311 assert_eq!(
312 get_backing_memory_validate_flags(vmo_flags, options),
313 Err(Status::ACCESS_DENIED)
314 );
315 }
316 if !writable {
317 let vmo_flags = rights_to_vmo_flags(readable, true, false);
318 assert_eq!(
319 get_backing_memory_validate_flags(vmo_flags, options),
320 Err(Status::ACCESS_DENIED)
321 );
322 }
323 if !executable {
324 let vmo_flags = rights_to_vmo_flags(readable, false, true);
325 assert_eq!(
326 get_backing_memory_validate_flags(vmo_flags, options),
327 Err(Status::ACCESS_DENIED)
328 );
329 }
330 }
331 }
332 }
333}