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 Ok(())
90}
91
92#[cfg(test)]
93mod tests {
94 use super::new_connection_validate_options;
95 use crate::file::FileOptions;
96 use crate::protocols::ToFileOptions;
97 use crate::test_utils::build_flag_combinations;
98
99 use assert_matches::assert_matches;
100 use fidl_fuchsia_io as fio;
101 use zx_status::Status;
102
103 fn options_to_rights(options: FileOptions) -> (bool, bool, bool) {
104 return (
105 options.rights.intersects(fio::Operations::READ_BYTES),
106 options.rights.intersects(fio::Operations::WRITE_BYTES),
107 options.rights.intersects(fio::Operations::EXECUTE),
108 );
109 }
110
111 fn ncvf(
112 flags: impl ToFileOptions,
113 readable: bool,
114 writable: bool,
115 executable: bool,
116 ) -> Result<FileOptions, Status> {
117 let options = flags.to_file_options()?;
118 new_connection_validate_options(&options, readable, writable, executable)?;
119 Ok(options)
120 }
121
122 #[test]
123 fn new_connection_validate_flags_create() {
124 for open_flags in build_flag_combinations(
125 fio::Flags::FLAG_MAYBE_CREATE,
126 fio::Flags::PERM_READ_BYTES
127 | fio::Flags::PERM_WRITE_BYTES
128 | fio::Flags::FLAG_MUST_CREATE,
129 ) {
130 let options = open_flags.to_file_options().unwrap();
131 let (readable, writable, executable) = options_to_rights(options);
132 assert_matches!(ncvf(options, readable, writable, executable), Ok(_));
133 }
134 }
135
136 #[test]
137 fn new_connection_validate_flags_truncate() {
138 assert_matches!(
139 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_TRUNCATE, true, true, false),
140 Err(Status::INVALID_ARGS)
141 );
142 assert_matches!(
143 ncvf(fio::Flags::PERM_WRITE_BYTES | fio::Flags::FILE_TRUNCATE, true, true, false),
144 Ok(_)
145 );
146 assert_matches!(
147 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_TRUNCATE, true, false, false),
148 Err(Status::INVALID_ARGS)
149 );
150 }
151
152 #[test]
153 fn new_connection_validate_flags_append() {
154 assert_matches!(
155 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_APPEND, true, false, false),
156 Ok(_)
157 );
158 assert_matches!(
159 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_APPEND, true, true, false),
160 Ok(_)
161 );
162 assert_matches!(
163 ncvf(fio::Flags::PERM_READ_BYTES | fio::Flags::FILE_APPEND, true, true, false),
164 Ok(_)
165 );
166 }
167
168 #[test]
169 fn new_connection_validate_flags_open_rights() {
170 for open_flags in build_flag_combinations(
171 fio::Flags::empty(),
172 fio::Flags::PERM_READ_BYTES | fio::Flags::PERM_READ_BYTES | fio::Flags::PERM_EXECUTE,
173 ) {
174 let options = open_flags.to_file_options().unwrap();
175 let (readable, writable, executable) = options_to_rights(options);
176
177 if !(writable && executable) {
180 assert_matches!(ncvf(options, readable, writable, executable), Ok(_));
181 }
182
183 if readable && !(writable && executable) {
185 assert_eq!(ncvf(options, false, writable, executable), Err(Status::ACCESS_DENIED));
186 }
187 if writable {
188 assert_eq!(ncvf(options, readable, false, executable), Err(Status::ACCESS_DENIED));
189 }
190 if executable {
191 assert_eq!(ncvf(options, readable, writable, false), Err(Status::ACCESS_DENIED));
192 }
193 }
194 }
195
196 #[cfg(target_os = "fuchsia")]
197 mod vmo_tests {
198 use super::super::{get_backing_memory_validate_flags, vmo_flags_to_rights};
199 use super::*;
200
201 fn rights_to_vmo_flags(readable: bool, writable: bool, executable: bool) -> fio::VmoFlags {
202 return if readable { fio::VmoFlags::READ } else { fio::VmoFlags::empty() }
203 | if writable { fio::VmoFlags::WRITE } else { fio::VmoFlags::empty() }
204 | if executable { fio::VmoFlags::EXECUTE } else { fio::VmoFlags::empty() };
205 }
206
207 #[test]
209 fn test_vmo_flags_to_rights() {
210 for vmo_flags in build_flag_combinations(
211 fio::VmoFlags::empty(),
212 fio::VmoFlags::READ | fio::VmoFlags::WRITE | fio::VmoFlags::EXECUTE,
213 ) {
214 let rights: fidl::Rights = vmo_flags_to_rights(vmo_flags);
215 assert_eq!(
216 vmo_flags.contains(fio::VmoFlags::READ),
217 rights.contains(fidl::Rights::READ)
218 );
219 assert_eq!(
220 vmo_flags.contains(fio::VmoFlags::WRITE),
221 rights.contains(fidl::Rights::WRITE)
222 );
223 assert_eq!(
224 vmo_flags.contains(fio::VmoFlags::EXECUTE),
225 rights.contains(fidl::Rights::EXECUTE)
226 );
227 }
228 }
229
230 #[test]
231 fn get_backing_memory_validate_flags_invalid() {
232 assert_eq!(
234 get_backing_memory_validate_flags(
235 fio::VmoFlags::PRIVATE_CLONE | fio::VmoFlags::SHARED_BUFFER,
236 fio::Flags::empty().to_file_options().unwrap()
237 ),
238 Err(Status::INVALID_ARGS)
239 );
240 }
241
242 #[test]
245 fn get_backing_memory_validate_flags_less_rights() {
246 for open_flags in build_flag_combinations(
247 fio::Flags::empty(),
248 fio::Flags::PERM_READ_BYTES
249 | fio::Flags::PERM_WRITE_BYTES
250 | fio::Flags::PERM_EXECUTE,
251 ) {
252 let options = open_flags.to_file_options().unwrap();
253 let (readable, writable, executable) = options_to_rights(options);
254 let vmo_flags = rights_to_vmo_flags(readable, writable, executable);
255
256 get_backing_memory_validate_flags(vmo_flags, options)
258 .expect("Failed to validate flags");
259
260 if readable {
262 let vmo_flags = rights_to_vmo_flags(false, writable, executable);
263 get_backing_memory_validate_flags(vmo_flags, options)
264 .expect("Failed to validate flags");
265 }
266 if writable {
267 let vmo_flags = rights_to_vmo_flags(readable, false, executable);
268 get_backing_memory_validate_flags(vmo_flags, options)
269 .expect("Failed to validate flags");
270 }
271 if executable {
272 let vmo_flags = rights_to_vmo_flags(readable, writable, false);
273 get_backing_memory_validate_flags(vmo_flags, options)
274 .expect("Failed to validate flags");
275 }
276 }
277 }
278
279 #[test]
281 fn get_backing_memory_validate_flags_more_rights() {
282 for open_flags in build_flag_combinations(
283 fio::Flags::empty(),
284 fio::Flags::PERM_READ_BYTES
285 | fio::Flags::PERM_WRITE_BYTES
286 | fio::Flags::PERM_EXECUTE,
287 ) {
288 let options = open_flags.to_file_options().unwrap();
289 let (readable, writable, executable) = options_to_rights(options);
291 if !readable {
292 let vmo_flags = rights_to_vmo_flags(true, writable, executable);
293 assert_eq!(
294 get_backing_memory_validate_flags(vmo_flags, options),
295 Err(Status::ACCESS_DENIED)
296 );
297 }
298 if !writable {
299 let vmo_flags = rights_to_vmo_flags(readable, true, false);
300 assert_eq!(
301 get_backing_memory_validate_flags(vmo_flags, options),
302 Err(Status::ACCESS_DENIED)
303 );
304 }
305 if !executable {
306 let vmo_flags = rights_to_vmo_flags(readable, false, true);
307 assert_eq!(
308 get_backing_memory_validate_flags(vmo_flags, options),
309 Err(Status::ACCESS_DENIED)
310 );
311 }
312 }
313 }
314 }
315}