1use crate::common::stricter_or_same_rights;
8use crate::directory::entry::EntryInfo;
9
10use byteorder::{LittleEndian, WriteBytesExt as _};
11use fidl_fuchsia_io as fio;
12use static_assertions::assert_eq_size;
13use std::io::Write as _;
14use std::mem::size_of;
15use zx_status::Status;
16
17pub(crate) fn check_child_connection_flags(
22 parent_flags: fio::OpenFlags,
23 mut flags: fio::OpenFlags,
24) -> Result<fio::OpenFlags, Status> {
25 if flags & (fio::OpenFlags::NOT_DIRECTORY | fio::OpenFlags::DIRECTORY)
26 == fio::OpenFlags::NOT_DIRECTORY | fio::OpenFlags::DIRECTORY
27 {
28 return Err(Status::INVALID_ARGS);
29 }
30
31 if flags.intersects(fio::OpenFlags::CREATE_IF_ABSENT)
33 && !flags.intersects(fio::OpenFlags::CREATE)
34 {
35 return Err(Status::INVALID_ARGS);
36 }
37
38 if flags.intersects(fio::OpenFlags::CLONE_SAME_RIGHTS) {
40 return Err(Status::INVALID_ARGS);
41 }
42
43 if !parent_flags.intersects(fio::OpenFlags::RIGHT_EXECUTABLE) {
45 flags &= !fio::OpenFlags::POSIX_EXECUTABLE;
46 }
47 if !parent_flags.intersects(fio::OpenFlags::RIGHT_WRITABLE) {
48 flags &= !fio::OpenFlags::POSIX_WRITABLE;
49 }
50
51 if flags.intersects(fio::OpenFlags::CREATE)
53 && !parent_flags.intersects(fio::OpenFlags::RIGHT_WRITABLE)
54 {
55 return Err(Status::ACCESS_DENIED);
56 }
57
58 if stricter_or_same_rights(parent_flags, flags) {
59 Ok(flags)
60 } else {
61 Err(Status::ACCESS_DENIED)
62 }
63}
64
65pub(crate) fn encode_dirent(
70 buf: &mut Vec<u8>,
71 max_bytes: u64,
72 entry: &EntryInfo,
73 name: &str,
74) -> bool {
75 let header_size = size_of::<u64>() + size_of::<u8>() + size_of::<u8>();
76
77 assert_eq_size!(u64, usize);
78
79 if buf.len() + header_size + name.len() > max_bytes as usize {
80 return false;
81 }
82
83 assert!(
84 name.len() <= fio::MAX_NAME_LENGTH as usize,
85 "Entry names are expected to be no longer than MAX_FILENAME ({}) bytes.\n\
86 Got entry: '{}'\n\
87 Length: {} bytes",
88 fio::MAX_NAME_LENGTH,
89 name,
90 name.len()
91 );
92
93 assert!(
94 fio::MAX_NAME_LENGTH <= u8::max_value() as u64,
95 "Expecting to be able to store MAX_FILENAME ({}) in one byte.",
96 fio::MAX_NAME_LENGTH
97 );
98
99 buf.write_u64::<LittleEndian>(entry.inode())
100 .expect("out should be an in memory buffer that grows as needed");
101 buf.write_u8(name.len() as u8).expect("out should be an in memory buffer that grows as needed");
102 buf.write_u8(entry.type_().into_primitive())
103 .expect("out should be an in memory buffer that grows as needed");
104 buf.write_all(name.as_ref()).expect("out should be an in memory buffer that grows as needed");
105
106 true
107}
108
109#[cfg(test)]
110mod tests {
111 use super::check_child_connection_flags;
112 use crate::test_utils::build_flag_combinations;
113 use crate::ProtocolsExt;
114
115 use fidl_fuchsia_io as fio;
116 use zx_status::Status;
117
118 fn new_connection_validate_flags(flags: fio::OpenFlags) -> Result<fio::OpenFlags, Status> {
119 flags
120 .to_directory_options()
121 .map(|options| options.to_io1() | (flags & fio::OpenFlags::DESCRIBE))
122 }
123
124 #[track_caller]
125 fn ncvf_ok(flags: fio::OpenFlags, expected_new_flags: fio::OpenFlags) {
126 let res = new_connection_validate_flags(flags);
127 match res {
128 Ok(new_flags) => assert_eq!(
129 expected_new_flags, new_flags,
130 "new_connection_validate_flags returned unexpected set of flags.\n\
131 Expected: {:X}\n\
132 Actual: {:X}",
133 expected_new_flags, new_flags
134 ),
135 Err(status) => panic!("new_connection_validate_flags failed. Status: {}", status),
136 }
137 }
138
139 #[track_caller]
140 fn ncvf_err(flags: fio::OpenFlags, expected_status: Status) {
141 let res = new_connection_validate_flags(flags);
142 match res {
143 Ok(new_flags) => panic!(
144 "new_connection_validate_flags should have failed. \
145 Got new flags: {:X}",
146 new_flags
147 ),
148 Err(status) => assert_eq!(expected_status, status),
149 }
150 }
151
152 #[test]
153 fn new_connection_validate_flags_posix() {
154 for open_flags in build_flag_combinations(
155 fio::OpenFlags::empty(),
156 fio::OpenFlags::RIGHT_READABLE
157 | fio::OpenFlags::POSIX_EXECUTABLE
158 | fio::OpenFlags::POSIX_WRITABLE,
159 ) {
160 let mut expected_rights = open_flags & fio::OpenFlags::RIGHT_READABLE;
161 if open_flags.intersects(fio::OpenFlags::POSIX_WRITABLE) {
162 expected_rights |= fio::OpenFlags::RIGHT_WRITABLE
163 }
164 if open_flags.intersects(fio::OpenFlags::POSIX_EXECUTABLE) {
165 expected_rights |= fio::OpenFlags::RIGHT_EXECUTABLE
166 }
167 ncvf_ok(open_flags, expected_rights);
168 }
169 }
170
171 #[test]
172 fn new_connection_validate_flags_append() {
173 ncvf_err(fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::APPEND, Status::INVALID_ARGS);
174 }
175
176 #[test]
177 fn new_connection_validate_flags_truncate() {
178 ncvf_err(fio::OpenFlags::RIGHT_WRITABLE | fio::OpenFlags::TRUNCATE, Status::INVALID_ARGS);
179 }
180
181 #[test]
182 fn check_child_connection_flags_create_flags() {
183 assert_eq!(
184 check_child_connection_flags(fio::OpenFlags::RIGHT_WRITABLE, fio::OpenFlags::CREATE,),
185 Ok(fio::OpenFlags::CREATE)
186 );
187 assert_eq!(
188 check_child_connection_flags(
189 fio::OpenFlags::RIGHT_WRITABLE,
190 fio::OpenFlags::CREATE | fio::OpenFlags::CREATE_IF_ABSENT,
191 ),
192 Ok(fio::OpenFlags::CREATE | fio::OpenFlags::CREATE_IF_ABSENT)
193 );
194
195 assert_eq!(
196 check_child_connection_flags(fio::OpenFlags::empty(), fio::OpenFlags::CREATE),
197 Err(Status::ACCESS_DENIED),
198 );
199 assert_eq!(
200 check_child_connection_flags(
201 fio::OpenFlags::empty(),
202 fio::OpenFlags::CREATE | fio::OpenFlags::CREATE_IF_ABSENT,
203 ),
204 Err(Status::ACCESS_DENIED),
205 );
206
207 assert_eq!(
209 check_child_connection_flags(
210 fio::OpenFlags::RIGHT_WRITABLE,
211 fio::OpenFlags::CREATE_IF_ABSENT,
212 ),
213 Err(Status::INVALID_ARGS),
214 );
215 }
216
217 #[test]
218 fn check_child_connection_flags_invalid() {
219 assert_eq!(
221 check_child_connection_flags(
222 fio::OpenFlags::empty(),
223 fio::OpenFlags::DIRECTORY | fio::OpenFlags::NOT_DIRECTORY,
224 ),
225 Err(Status::INVALID_ARGS),
226 );
227
228 assert_eq!(
230 check_child_connection_flags(
231 fio::OpenFlags::empty(),
232 fio::OpenFlags::CLONE_SAME_RIGHTS,
233 ),
234 Err(Status::INVALID_ARGS),
235 );
236 }
237}