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