1use crate::directory::FatDirectory;
6use crate::filesystem::{FatFilesystem, FatFilesystemInner};
7use crate::node::Node;
8use crate::refs::{FatfsFileRef, Guard, GuardMut, Wrapper};
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::RefCell;
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: RefCell<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: RefCell::new(file),
85 filesystem,
86 data: RwLock::new(FatFileData { parent: Some(parent), name }),
87 })
88 }
89
90 pub(crate) fn borrow_file_mut<'a>(
92 &'a self,
93 fs: &'a FatFilesystemInner,
94 ) -> Option<GuardMut<'a, FatfsFileRef>> {
95 let mut file = self.file.borrow_mut();
96 if file.get_mut(fs).is_none() {
97 None
98 } else {
99 Some(GuardMut::new(fs, file))
100 }
101 }
102
103 pub fn borrow_file<'a>(
104 &'a self,
105 fs: &'a FatFilesystemInner,
106 ) -> Result<Guard<'a, FatfsFileRef>, Status> {
107 let file = self.file.borrow();
108 if file.get(fs).is_none() {
109 Err(Status::BAD_HANDLE)
110 } else {
111 Ok(Guard::new(fs, file))
112 }
113 }
114
115 async fn write_or_append(
116 &self,
117 offset: Option<u64>,
118 content: &[u8],
119 ) -> Result<(u64, u64), Status> {
120 let fs_lock = self.filesystem.lock();
121 let mut file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
122 let mut file_offset = match offset {
123 Some(offset) => {
124 seek_for_write(&mut *file, offset)?;
125 offset
126 }
127 None => file.seek(std::io::SeekFrom::End(0)).map_err(fatfs_error_to_status)?,
128 };
129 let mut total_written = 0;
130 while total_written < content.len() {
131 let written = file.write(&content[total_written..]).map_err(fatfs_error_to_status)?;
132 if written == 0 {
133 break;
134 }
135 total_written += written;
136 file_offset += written as u64;
137 let result = file.write(&content[total_written..]).map_err(fatfs_error_to_status);
138 match result {
139 Ok(0) => break,
140 Ok(written) => {
141 total_written += written;
142 file_offset += written as u64;
143 }
144 Err(e) => {
145 if total_written > 0 {
146 break;
147 }
148 return Err(e);
149 }
150 }
151 }
152 self.filesystem.mark_dirty();
153 Ok((total_written as u64, file_offset))
154 }
155}
156
157impl Node for FatFile {
158 fn detach(&self, fs: &FatFilesystemInner) {
161 self.file.borrow_mut().take(fs);
162 }
163
164 fn attach(
166 &self,
167 new_parent: Arc<FatDirectory>,
168 name: &str,
169 fs: &FatFilesystemInner,
170 ) -> Result<(), Status> {
171 let mut data = self.data.write();
172 data.name = name.to_owned();
173 let mut file = self.file.borrow_mut();
175 unsafe { file.maybe_reopen(fs, &new_parent, name)? };
177 data.parent.replace(new_parent);
178 Ok(())
179 }
180
181 fn did_delete(&self) {
182 self.data.write().parent.take();
183 }
184
185 fn open_ref(&self, fs_lock: &FatFilesystemInner) -> Result<(), Status> {
186 let data = self.data.read();
187 let mut file_ref = self.file.borrow_mut();
188 unsafe { file_ref.open(&fs_lock, data.parent.as_deref(), &data.name) }
189 }
190
191 fn shut_down(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
193 self.file.borrow_mut().take(fs);
194 Ok(())
195 }
196
197 fn flush_dir_entry(&self, fs: &FatFilesystemInner) -> Result<(), Status> {
198 if let Some(mut file) = self.borrow_file_mut(fs) {
199 file.flush_dir_entry().map_err(fatfs_error_to_status)?
200 }
201 Ok(())
202 }
203
204 fn close_ref(&self, fs: &FatFilesystemInner) {
205 self.file.borrow_mut().close(fs);
206 }
207}
208
209impl vfs::node::Node for FatFile {
210 async fn get_attributes(
211 &self,
212 requested_attributes: fio::NodeAttributesQuery,
213 ) -> Result<fio::NodeAttributes2, Status> {
214 let fs_lock = self.filesystem.lock();
215 let file = self.borrow_file(&fs_lock)?;
216 let content_size = file.len() as u64;
217 let creation_time = dos_to_unix_time(file.created());
218 let modification_time = dos_to_unix_time(file.modified());
219 let access_time = dos_date_to_unix_time(file.accessed());
220
221 let cluster_size = fs_lock.cluster_size() as u64;
224 let storage_size = ((content_size + cluster_size - 1) / cluster_size) * cluster_size;
225
226 Ok(attributes!(
227 requested_attributes,
228 Mutable {
229 creation_time: creation_time,
230 modification_time: modification_time,
231 access_time: access_time
232 },
233 Immutable {
234 protocols: fio::NodeProtocolKinds::FILE,
235 abilities: fio::Operations::GET_ATTRIBUTES
236 | fio::Operations::UPDATE_ATTRIBUTES
237 | fio::Operations::READ_BYTES
238 | fio::Operations::WRITE_BYTES,
239 content_size: content_size,
240 storage_size: storage_size,
241 link_count: 1, }
243 ))
244 }
245
246 fn close(self: Arc<Self>) {
247 self.close_ref(&self.filesystem.lock());
248 }
249
250 fn query_filesystem(&self) -> Result<fio::FilesystemInfo, Status> {
251 self.filesystem.query_filesystem()
252 }
253
254 fn will_clone(&self) {
255 self.open_ref(&self.filesystem.lock()).unwrap();
256 }
257}
258
259impl Debug for FatFile {
260 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261 f.debug_struct("FatFile").field("name", &self.data.read().name).finish()
262 }
263}
264
265impl VfsFile for FatFile {
266 fn writable(&self) -> bool {
267 return true;
268 }
269
270 async fn open_file(&self, _options: &FileOptions) -> Result<(), Status> {
271 Ok(())
272 }
273
274 async fn truncate(&self, length: u64) -> Result<(), Status> {
275 let fs_lock = self.filesystem.lock();
276 let mut file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
277 seek_for_write(&mut *file, length)?;
278 file.truncate().map_err(fatfs_error_to_status)?;
279 self.filesystem.mark_dirty();
280 Ok(())
281 }
282
283 #[allow(deprecated)]
288 async fn update_attributes(
289 &self,
290 attributes: fio::MutableNodeAttributes,
291 ) -> Result<(), Status> {
292 const SUPPORTED_MUTABLE_ATTRIBUTES: fio::NodeAttributesQuery =
293 fio::NodeAttributesQuery::CREATION_TIME
294 .union(fio::NodeAttributesQuery::MODIFICATION_TIME);
295
296 if !SUPPORTED_MUTABLE_ATTRIBUTES
297 .contains(vfs::common::mutable_node_attributes_to_query(&attributes))
298 {
299 return Err(Status::NOT_SUPPORTED);
300 }
301
302 let fs_lock = self.filesystem.lock();
303 let mut file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
304 let mut needs_flush = false;
305 if let Some(creation_time) = attributes.creation_time {
306 file.set_created(unix_to_dos_time(creation_time));
307 needs_flush = true;
308 }
309 if let Some(modification_time) = attributes.modification_time {
310 file.set_modified(unix_to_dos_time(modification_time));
311 needs_flush = true;
312 }
313
314 if needs_flush {
315 file.flush().map_err(fatfs_error_to_status)?;
316 self.filesystem.mark_dirty();
317 }
318 Ok(())
319 }
320
321 async fn get_size(&self) -> Result<u64, Status> {
322 let fs_lock = self.filesystem.lock();
323 let file = self.borrow_file(&fs_lock)?;
324 Ok(file.len() as u64)
325 }
326
327 async fn sync(&self, _mode: SyncMode) -> Result<(), Status> {
328 let fs_lock = self.filesystem.lock();
329 let mut file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
330
331 file.flush().map_err(fatfs_error_to_status)?;
332 Ok(())
333 }
334}
335
336impl VfsFileIo for FatFile {
337 async fn read_at(&self, offset: u64, buffer: &mut [u8]) -> Result<u64, Status> {
338 let fs_lock = self.filesystem.lock();
339 let mut file = self.borrow_file_mut(&fs_lock).ok_or(Status::BAD_HANDLE)?;
340
341 let real_offset =
342 file.seek(std::io::SeekFrom::Start(offset)).map_err(fatfs_error_to_status)?;
343 if real_offset != offset {
346 return Ok(0);
347 }
348 let mut total_read = 0;
349 while total_read < buffer.len() {
350 let read = file.read(&mut buffer[total_read..]).map_err(fatfs_error_to_status)?;
351 if read == 0 {
352 break;
353 }
354 total_read += read;
355 }
356 Ok(total_read as u64)
357 }
358
359 async fn write_at(&self, offset: u64, content: &[u8]) -> Result<u64, Status> {
360 self.write_or_append(Some(offset), content).await.map(|r| r.0)
361 }
362
363 async fn append(&self, content: &[u8]) -> Result<(u64, u64), Status> {
364 self.write_or_append(None, content).await
365 }
366}
367
368impl vfs::directory::entry::GetEntryInfo for FatFile {
369 fn entry_info(&self) -> EntryInfo {
370 EntryInfo::new(fio::INO_UNKNOWN, fio::DirentType::File)
371 }
372}
373
374#[cfg(test)]
375mod tests {
376 use super::*;
378 use crate::node::{Closer, FatNode};
379 use crate::tests::{TestDiskContents, TestFatDisk};
380
381 const TEST_DISK_SIZE: u64 = 2048 << 10; const TEST_FILE_CONTENT: &str = "test file contents";
383
384 struct TestFile(Arc<FatFile>);
385
386 impl TestFile {
387 fn new() -> Self {
388 let disk = TestFatDisk::empty_disk(TEST_DISK_SIZE);
389 let structure =
390 TestDiskContents::dir().add_child("test_file", TEST_FILE_CONTENT.into());
391 structure.create(&disk.root_dir());
392
393 let fs = disk.into_fatfs();
394 let dir = fs.get_fatfs_root();
395 let mut closer = Closer::new(&fs.filesystem());
396 dir.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
397 closer.add(FatNode::Dir(dir.clone()));
398 let file = match dir
399 .open_child("test_file", fio::OpenFlags::empty(), &mut closer)
400 .expect("Open to succeed")
401 {
402 FatNode::File(f) => f,
403 val => panic!("Unexpected value {:?}", val),
404 };
405 file.open_ref(&fs.filesystem().lock()).expect("open_ref failed");
406 TestFile(file)
407 }
408 }
409
410 impl Drop for TestFile {
411 fn drop(&mut self) {
412 self.0.close_ref(&self.0.filesystem.lock());
413 }
414 }
415
416 impl std::ops::Deref for TestFile {
417 type Target = Arc<FatFile>;
418
419 fn deref(&self) -> &Self::Target {
420 &self.0
421 }
422 }
423
424 #[fuchsia::test]
425 async fn test_read_at() {
426 let file = TestFile::new();
427 let mut buffer = [0u8; 512];
433 let err = file.read_at(u64::MAX - 30, &mut buffer).await.expect_err("Read fails");
434 assert_eq!(err, Status::INVALID_ARGS);
435 }
436
437 #[fuchsia::test]
438 async fn test_get_attributes() {
439 let file = TestFile::new();
440 let fio::NodeAttributes2 { mutable_attributes, immutable_attributes } =
441 vfs::node::Node::get_attributes(&**file, fio::NodeAttributesQuery::all())
442 .await
443 .unwrap();
444 assert_eq!(immutable_attributes.content_size.unwrap(), TEST_FILE_CONTENT.len() as u64);
445 assert!(immutable_attributes.storage_size.unwrap() > TEST_FILE_CONTENT.len() as u64);
446 assert_eq!(immutable_attributes.protocols.unwrap(), fio::NodeProtocolKinds::FILE);
447 assert_eq!(
448 immutable_attributes.abilities.unwrap(),
449 fio::Abilities::GET_ATTRIBUTES
450 | fio::Abilities::UPDATE_ATTRIBUTES
451 | fio::Abilities::READ_BYTES
452 | fio::Abilities::WRITE_BYTES
453 );
454 assert!(mutable_attributes.creation_time.is_some());
455 assert!(mutable_attributes.modification_time.is_some());
456 }
457
458 #[fuchsia::test]
459 async fn test_update_attributes() {
460 let file = TestFile::new();
461
462 let new_time = std::time::SystemTime::now()
463 .duration_since(std::time::SystemTime::UNIX_EPOCH)
464 .expect("SystemTime before UNIX EPOCH")
465 .as_nanos();
466 let new_attrs = fio::MutableNodeAttributes {
467 creation_time: Some(new_time.try_into().unwrap()),
468 ..Default::default()
469 };
470 file.update_attributes(new_attrs).await.expect("update attributes failed");
471 }
472}