1use crate::directory::FatDirectory;
6use crate::filesystem::{FatFilesystem, FatFilesystemInner};
7use crate::node::Node;
8use crate::refs::FatfsFileRef;
9use crate::types::File;
10use crate::util::{
11 dos_date_to_unix_time, dos_to_unix_time, fatfs_error_to_status, unix_to_dos_time,
12};
13use fidl_fuchsia_io as fio;
14use fuchsia_sync::RwLock;
15use std::cell::UnsafeCell;
16use std::fmt::Debug;
17use std::io::{Read, Seek, Write};
18use std::pin::Pin;
19use std::sync::Arc;
20use vfs::attributes;
21use vfs::directory::entry::EntryInfo;
22use vfs::file::{File as VfsFile, FileIo as VfsFileIo, FileOptions, SyncMode};
23use zx::{self as zx, Status};
24
25fn extend(file: &mut File<'_>, mut current: u64, target: u64) -> Result<(), Status> {
26 let zeros = vec![0; 8192];
27 while current < target {
28 let to_do = (std::cmp::min(target, (current + 8192) / 8192 * 8192) - current) as usize;
29 let written = file.write(&zeros[..to_do]).map_err(fatfs_error_to_status)? as u64;
30 if written == 0 {
31 return Err(Status::NO_SPACE);
32 }
33 current += written;
34 }
35 Ok(())
36}
37
38fn seek_for_write(file: &mut File<'_>, offset: u64) -> Result<(), Status> {
39 if offset > fatfs::MAX_FILE_SIZE as u64 {
40 return Err(Status::INVALID_ARGS);
41 }
42 let real_offset = file.seek(std::io::SeekFrom::Start(offset)).map_err(fatfs_error_to_status)?;
43 if real_offset == offset {
44 return Ok(());
45 }
46 assert!(real_offset < offset);
47 let result = extend(file, real_offset, offset);
48 if let Err(e) = result {
49 file.seek(std::io::SeekFrom::Start(real_offset)).map_err(fatfs_error_to_status)?;
51 file.truncate().map_err(fatfs_error_to_status)?;
52 return Err(e);
53 }
54 Ok(())
55}
56
57struct FatFileData {
58 name: String,
59 parent: Option<Arc<FatDirectory>>,
60}
61
62pub struct FatFile {
64 file: UnsafeCell<FatfsFileRef>,
65 filesystem: Pin<Arc<FatFilesystem>>,
66 data: RwLock<FatFileData>,
67}
68
69unsafe impl Sync for FatFile {}
73unsafe impl Send for FatFile {}
74
75impl FatFile {
76 pub(crate) fn new(
78 file: FatfsFileRef,
79 parent: Arc<FatDirectory>,
80 filesystem: Pin<Arc<FatFilesystem>>,
81 name: String,
82 ) -> Arc<Self> {
83 Arc::new(FatFile {
84 file: UnsafeCell::new(file),
85 filesystem,
86 data: RwLock::new(FatFileData { parent: Some(parent), name }),
87 })
88 }
89
90 pub(crate) fn borrow_file_mut<'a>(&self, fs: &'a FatFilesystemInner) -> Option<&mut File<'a>> {
92 unsafe { self.file.get().as_mut() }.unwrap().borrow_mut(fs)
94 }
95
96 pub fn borrow_file<'a>(&self, fs: &'a FatFilesystemInner) -> Result<&File<'a>, Status> {
97 unsafe { self.file.get().as_ref() }.unwrap().borrow(fs).ok_or(Status::BAD_HANDLE)
99 }
100
101 async fn write_or_append(
102 &self,
103 offset: Option<u64>,
104 content: &[u8],
105 ) -> Result<(u64, u64), Status> {
106 let fs_lock = self.filesystem.lock();
107 let file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
108 let mut file_offset = match offset {
109 Some(offset) => {
110 seek_for_write(file, offset)?;
111 offset
112 }
113 None => file.seek(std::io::SeekFrom::End(0)).map_err(fatfs_error_to_status)?,
114 };
115 let mut total_written = 0;
116 while total_written < content.len() {
117 let written = file.write(&content[total_written..]).map_err(fatfs_error_to_status)?;
118 if written == 0 {
119 break;
120 }
121 total_written += written;
122 file_offset += written as u64;
123 let result = file.write(&content[total_written..]).map_err(fatfs_error_to_status);
124 match result {
125 Ok(0) => break,
126 Ok(written) => {
127 total_written += written;
128 file_offset += written as u64;
129 }
130 Err(e) => {
131 if total_written > 0 {
132 break;
133 }
134 return Err(e);
135 }
136 }
137 }
138 self.filesystem.mark_dirty();
139 Ok((total_written as u64, file_offset))
140 }
141}
142
143impl Node for FatFile {
144 fn detach(&self, fs: &FatFilesystemInner) {
147 let file = unsafe { self.file.get().as_mut() }.unwrap();
149 file.take(fs);
151 }
152
153 fn attach(
155 &self,
156 new_parent: Arc<FatDirectory>,
157 name: &str,
158 fs: &FatFilesystemInner,
159 ) -> Result<(), Status> {
160 let mut data = self.data.write();
161 data.name = name.to_owned();
162 let file = unsafe { self.file.get().as_mut() }.unwrap();
164 unsafe { file.maybe_reopen(fs, &new_parent, name)? };
166 data.parent.replace(new_parent);
167 Ok(())
168 }
169
170 fn did_delete(&self) {
171 self.data.write().parent.take();
172 }
173
174 fn open_ref(&self, fs_lock: &FatFilesystemInner) -> Result<(), Status> {
175 let data = self.data.read();
176 let file_ref = unsafe { self.file.get().as_mut() }.unwrap();
177 unsafe { file_ref.open(&fs_lock, data.parent.as_deref(), &data.name) }
178 }
179
180 fn shut_down(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
182 unsafe { self.file.get().as_mut() }.unwrap().take(fs);
183 Ok(())
184 }
185
186 fn flush_dir_entry(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
187 if let Some(file) = self.borrow_file_mut(fs) {
188 file.flush_dir_entry().map_err(fatfs_error_to_status)?
189 }
190 Ok(())
191 }
192
193 fn close_ref(&self, fs: &FatFilesystemInner) {
194 unsafe { self.file.get().as_mut() }.unwrap().close(fs);
195 }
196}
197
198impl vfs::node::Node for FatFile {
199 async fn get_attributes(
200 &self,
201 requested_attributes: fio::NodeAttributesQuery,
202 ) -> Result<fio::NodeAttributes2, Status> {
203 let fs_lock = self.filesystem.lock();
204 let file = self.borrow_file(&fs_lock)?;
205 let content_size = file.len() as u64;
206 let creation_time = dos_to_unix_time(file.created());
207 let modification_time = dos_to_unix_time(file.modified());
208 let access_time = dos_date_to_unix_time(file.accessed());
209
210 let cluster_size = fs_lock.cluster_size() as u64;
213 let storage_size = ((content_size + cluster_size - 1) / cluster_size) * cluster_size;
214
215 Ok(attributes!(
216 requested_attributes,
217 Mutable {
218 creation_time: creation_time,
219 modification_time: modification_time,
220 access_time: access_time
221 },
222 Immutable {
223 protocols: fio::NodeProtocolKinds::FILE,
224 abilities: fio::Operations::GET_ATTRIBUTES
225 | fio::Operations::UPDATE_ATTRIBUTES
226 | fio::Operations::READ_BYTES
227 | fio::Operations::WRITE_BYTES,
228 content_size: content_size,
229 storage_size: storage_size,
230 link_count: 1, }
232 ))
233 }
234
235 fn close(self: Arc<Self>) {
236 self.close_ref(&self.filesystem.lock());
237 }
238
239 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
240 self.filesystem.query_filesystem()
241 }
242
243 fn will_clone(&self) {
244 self.open_ref(&self.filesystem.lock()).unwrap();
245 }
246}
247
248impl Debug for FatFile {
249 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
250 f.debug_struct("FatFile").field("name", &self.data.read().name).finish()
251 }
252}
253
254impl VfsFile for FatFile {
255 fn writable(&self) -> bool {
256 return true;
257 }
258
259 async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
260 Ok(())
261 }
262
263 async fn truncate(&self, length: u64) -> Result<(), Status> {
264 let fs_lock = self.filesystem.lock();
265 let file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
266 seek_for_write(file, length)?;
267 file.truncate().map_err(fatfs_error_to_status)?;
268 self.filesystem.mark_dirty();
269 Ok(())
270 }
271
272 async fn get_backing_memory(&self, _flags: fio::VmoFlags) -> Result<zx::Vmo, Status> {
273 Err(Status::NOT_SUPPORTED)
274 }
275
276 #[allow(deprecated)]
281 async fn update_attributes(
282 &self,
283 attributes: fio::MutableNodeAttributes,
284 ) -> Result<(), Status> {
285 const SUPPORTED_MUTABLE_ATTRIBUTES: fio::NodeAttributesQuery =
286 fio::NodeAttributesQuery::CREATION_TIME
287 .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
288
289 if !SUPPORTED_MUTABLE_ATTRIBUTES
290 .contains(vfs::common::mutable_node_attributes_to_query(&attributes))
291 {
292 return Err(Status::NOT_SUPPORTED);
293 }
294
295 let fs_lock = self.filesystem.lock();
296 let file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
297 let mut needs_flush = false;
298 if let Some(creation_time) = attributes.creation_time {
299 file.set_created(unix_to_dos_time(creation_time));
300 needs_flush = true;
301 }
302 if let Some(modification_time) = attributes.modification_time {
303 file.set_modified(unix_to_dos_time(modification_time));
304 needs_flush = true;
305 }
306
307 if needs_flush {
308 file.flush().map_err(fatfs_error_to_status)?;
309 self.filesystem.mark_dirty();
310 }
311 Ok(())
312 }
313
314 async fn get_size(&self) -> Result<u64, Status> {
315 let fs_lock = self.filesystem.lock();
316 let file = self.borrow_file(&fs_lock)?;
317 Ok(file.len() as u64)
318 }
319
320 async fn sync(&self, _mode: SyncMode) -> Result<(), Status> {
321 let fs_lock = self.filesystem.lock();
322 let file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
323
324 file.flush().map_err(fatfs_error_to_status)?;
325 Ok(())
326 }
327}
328
329impl VfsFileIo for FatFile {
330 async fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<u64, Status> {
331 let fs_lock = self.filesystem.lock();
332 let file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
333
334 let real_offset =
335 file.seek(std::io::SeekFrom::Start(offset)).map_err(fatfs_error_to_status)?;
336 if real_offset != offset {
339 return Ok(0);
340 }
341 let mut total_read = 0;
342 while total_read < buffer.len() {
343 let read = file.read(&mut buffer[total_read..]).map_err(fatfs_error_to_status)?;
344 if read == 0 {
345 break;
346 }
347 total_read += read;
348 }
349 Ok(total_read as u64)
350 }
351
352 async fn write_at(&self, offset: u64, content: &[u8]) -> Result<u64, Status> {
353 self.write_or_append(Some(offset), content).await.map(|r| r.0)
354 }
355
356 async fn append(&self, content: &[u8]) -> Result<(u64, u64), Status> {
357 self.write_or_append(None, content).await
358 }
359}
360
361impl vfs::directory::entry::GetEntryInfo for FatFile {
362 fn entry_info(&self) -> EntryInfo {
363 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)
364 }
365}
366
367#[cfg(test)]
368mod tests {
369 use super::*;
371 use crate::node::{Closer, FatNode};
372 use crate::tests::{TestDiskContents, TestFatDisk};
373
374 const TEST_DISK_SIZE: u64 = 2048 << 10; const TEST_FILE_CONTENT: &str = "test file contents";
376
377 struct TestFile(Arc<FatFile>);
378
379 impl TestFile {
380 fn new() -> Self {
381 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
382 let structure =
383 TestDiskContents::dir().add_child("test_file", TEST_FILE_CONTENT.into());
384 structure.create(&disk.root_dir());
385
386 let fs = disk.into_fatfs();
387 let dir = fs.get_fatfs_root();
388 let mut closer = Closer::new(&fs.filesystem());
389 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
390 closer.add(FatNode::Dir(dir.clone()));
391 let file = match dir
392 .open_child("test_file", fio::OpenFlags::empty(), &mut closer)
393 .expect("Open to succeed")
394 {
395 FatNode::File(f) => f,
396 val => panic!("Unexpected value {:?}", val),
397 };
398 file.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
399 TestFile(file)
400 }
401 }
402
403 impl Drop for TestFile {
404 fn drop(&mut self) {
405 self.0.close_ref(&self.0.filesystem.lock());
406 }
407 }
408
409 impl std::ops::Deref for TestFile {
410 type Target = Arc<FatFile>;
411
412 fn deref(&self) -> &Self::Target {
413 &self.0
414 }
415 }
416
417 #[fuchsia::test]
418 async fn test_read_at() {
419 let file = TestFile::new();
420 let mut buffer = [0u8; 512];
426 let err = file.read_at(u64::MAX - 30, &mut buffer).await.expect_err("Read fails");
427 assert_eq!(err, Status::INVALID_ARGS);
428 }
429
430 #[fuchsia::test]
431 async fn test_get_attributes() {
432 let file = TestFile::new();
433 let fio::NodeAttributes2 { mutable_attributes, immutable_attributes } =
434 vfs::node::Node::get_attributes(&**file, fio::NodeAttributesQuery::all())
435 .await
436 .unwrap();
437 assert_eq!(immutable_attributes.content_size.unwrap(), TEST_FILE_CONTENT.len() as u64);
438 assert!(immutable_attributes.storage_size.unwrap() > TEST_FILE_CONTENT.len() as u64);
439 assert_eq!(immutable_attributes.protocols.unwrap(), fio::NodeProtocolKinds::FILE);
440 assert_eq!(
441 immutable_attributes.abilities.unwrap(),
442 fio::Abilities::GET_ATTRIBUTES
443 | fio::Abilities::UPDATE_ATTRIBUTES
444 | fio::Abilities::READ_BYTES
445 | fio::Abilities::WRITE_BYTES
446 );
447 assert!(mutable_attributes.creation_time.is_some());
448 assert!(mutable_attributes.modification_time.is_some());
449 }
450
451 #[fuchsia::test]
452 async fn test_update_attributes() {
453 let file = TestFile::new();
454
455 let new_time = std::time::SystemTime::now()
456 .duration_since(std::time::SystemTime::UNIX_EPOCH)
457 .expect("SystemTime before UNIX EPOCH")
458 .as_nanos();
459 let new_attrs = fio::MutableNodeAttributes {
460 creation_time: Some(new_time.try_into().unwrap()),
461 ..Default::default()
462 };
463 file.update_attributes(new_attrs).await.expect("update attributes failed");
464 }
465}