1 // Copyright (c) 2023 Huawei Device Co., Ltd. 2 // Licensed under the Apache License, Version 2.0 (the "License"); 3 // you may not use this file except in compliance with the License. 4 // You may obtain a copy of the License at 5 // 6 // http://www.apache.org/licenses/LICENSE-2.0 7 // 8 // Unless required by applicable law or agreed to in writing, software 9 // distributed under the License is distributed on an "AS IS" BASIS, 10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 use std::fmt::{Debug, Formatter}; 15 use std::fs::{File as SyncFile, Metadata, Permissions}; 16 use std::future::Future; 17 use std::io; 18 use std::io::{Seek, SeekFrom}; 19 use std::path::Path; 20 use std::pin::Pin; 21 use std::sync::Arc; 22 use std::task::{Context, Poll}; 23 24 use crate::fs::file_buf::FileBuf; 25 use crate::fs::{async_op, poll_ready}; 26 use crate::futures::poll_fn; 27 use crate::io::{AsyncRead, AsyncSeek, AsyncWrite, ReadBuf}; 28 use crate::spawn::spawn_blocking; 29 use crate::sync::Mutex; 30 use crate::task::{JoinHandle, TaskBuilder}; 31 32 /// An asynchronous wrapping of [`std::fs::File`]. Provides async read/write 33 /// methods. 34 pub struct File { 35 file: Arc<SyncFile>, 36 inner: Mutex<FileInner>, 37 // controls how many bytes the buffer could reserve during read 38 // and how many bytes the buffer could extend per time during write 39 buf_size_limit: usize, 40 } 41 42 struct FileInner { 43 state: FileState, 44 write_err: Option<io::ErrorKind>, 45 idx: u64, 46 } 47 48 type RWJoinHandle = JoinHandle<(FileBuf, io::Result<()>)>; 49 50 type SeekJoinHandle = JoinHandle<(FileBuf, io::Result<u64>)>; 51 52 enum FileState { 53 Idle(Option<FileBuf>), 54 Reading(RWJoinHandle), 55 Writing(RWJoinHandle), 56 Seeking(SeekJoinHandle), 57 } 58 59 enum FileOp { 60 Reading, 61 Writing, 62 Seeking, 63 } 64 65 impl Future for FileState { 66 type Output = io::Result<u64>; 67 poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>68 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { 69 let state = self.get_mut(); 70 match state { 71 FileState::Idle(_) => unreachable!(), 72 FileState::Reading(x) | FileState::Writing(x) => { 73 let (file_buf, res) = poll_ready!(Pin::new(x).poll(cx))?; 74 *state = FileState::Idle(Some(file_buf)); 75 // For read and write, we dont care about the output 76 Poll::Ready(res.map(|_| 0_u64)) 77 } 78 FileState::Seeking(x) => { 79 let (file_buf, res) = poll_ready!(Pin::new(x).poll(cx))?; 80 *state = FileState::Idle(Some(file_buf)); 81 Poll::Ready(res) 82 } 83 } 84 } 85 } 86 87 impl FileState { 88 #[inline] get_op(&self) -> FileOp89 fn get_op(&self) -> FileOp { 90 match self { 91 FileState::Idle(_) => unreachable!(), 92 FileState::Reading(_) => FileOp::Reading, 93 FileState::Writing(_) => FileOp::Writing, 94 FileState::Seeking(_) => FileOp::Seeking, 95 } 96 } 97 } 98 99 const DEFAULT_BUF_LIMIT: usize = 2 * 1024 * 1024; 100 101 impl Debug for File { fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result102 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 103 f.debug_tuple("YlongFile").field(&self.file).finish() 104 } 105 } 106 107 impl File { 108 /// Creates a new [`File`] struct. new(file: SyncFile) -> File109 pub fn new(file: SyncFile) -> File { 110 File { 111 file: Arc::new(file), 112 inner: Mutex::new(FileInner { 113 state: FileState::Idle(Some(FileBuf::with_capacity(0))), 114 write_err: None, 115 idx: 0, 116 }), 117 buf_size_limit: DEFAULT_BUF_LIMIT, 118 } 119 } 120 121 /// Attempts to open a file in read-only mode asynchronously. 122 /// 123 /// See the [`super::OpenOptions::open`] method for more details. 124 /// 125 /// # Errors 126 /// 127 /// This function will return an error if `path` does not already exist. 128 /// 129 /// # Examples 130 /// 131 /// ```no_run 132 /// use ylong_runtime::fs::File; 133 /// 134 /// async fn open() -> std::io::Result<()> { 135 /// let mut f = File::open("foo.txt").await?; 136 /// Ok(()) 137 /// } 138 /// ``` open<P: AsRef<Path>>(path: P) -> io::Result<File>139 pub async fn open<P: AsRef<Path>>(path: P) -> io::Result<File> { 140 let path = path.as_ref().to_owned(); 141 let file = async_op(|| SyncFile::open(path)).await?; 142 Ok(File::new(file)) 143 } 144 145 /// Opens a file in write-only mode asynchronously. 146 /// 147 /// This function will create a file if it does not exist 148 /// and truncate it if it does. 149 /// 150 /// 151 /// # Examples 152 /// 153 /// ```no_run 154 /// use ylong_runtime::fs::File; 155 /// 156 /// async fn create() -> std::io::Result<()> { 157 /// let mut f = File::create("foo.txt").await?; 158 /// Ok(()) 159 /// } 160 /// ``` create<P: AsRef<Path>>(path: P) -> io::Result<File>161 pub async fn create<P: AsRef<Path>>(path: P) -> io::Result<File> { 162 let path = path.as_ref().to_owned(); 163 let file = async_op(|| SyncFile::create(path)).await?; 164 Ok(File::new(file)) 165 } 166 167 /// Changes the permissions on the underlying file asynchronously. 168 /// 169 /// # Errors 170 /// This function will return an error if the user lacks permission change 171 /// attributes on the underlying file. It may also return an error in other 172 /// os-specific unspecified cases. 173 /// 174 /// # Examples 175 /// 176 /// ```no_run 177 /// use ylong_runtime::fs::File; 178 /// 179 /// async fn set_permissions() -> std::io::Result<()> { 180 /// let file = File::open("foo.txt").await?; 181 /// let mut perms = file.metadata().await?.permissions(); 182 /// perms.set_readonly(true); 183 /// file.set_permissions(perms).await?; 184 /// Ok(()) 185 /// } 186 /// ``` 187 /// 188 /// Note that this method alters the permissions of the underlying file, 189 /// even though it takes `&self` rather than `&mut self`. set_permissions(&self, perm: Permissions) -> io::Result<()>190 pub async fn set_permissions(&self, perm: Permissions) -> io::Result<()> { 191 let file = self.file.clone(); 192 async_op(move || file.set_permissions(perm)).await 193 } 194 195 /// Attempts to sync all OS-internal metadata to disk asynchronously. 196 /// 197 /// This function will attempt to ensure that all in-memory data reaches the 198 /// filesystem before returning. 199 /// 200 /// This can be used to handle errors that would otherwise only be caught 201 /// when the `File` is closed. Dropping a file will ignore errors in 202 /// synchronizing this in-memory data. 203 /// 204 /// # Examples 205 /// 206 /// ```no_run 207 /// use ylong_runtime::fs::File; 208 /// use ylong_runtime::io::AsyncWriteExt; 209 /// 210 /// async fn sync_all() -> std::io::Result<()> { 211 /// let mut f = File::create("foo.txt").await?; 212 /// f.write_all(b"Hello, world!").await?; 213 /// 214 /// f.sync_all().await?; 215 /// Ok(()) 216 /// } 217 /// ``` sync_all(&self) -> io::Result<()>218 pub async fn sync_all(&self) -> io::Result<()> { 219 let mut file = self.inner.lock().await; 220 file.flush().await; 221 let file = self.file.clone(); 222 async_op(move || file.sync_all()).await 223 } 224 225 /// This function is similar to [`File::sync_all`], except that it might not 226 /// synchronize file metadata to the filesystem. 227 /// 228 /// This is intended for use cases that must synchronize content, but don't 229 /// need the metadata on disk. The goal of this method is to reduce disk 230 /// operations. 231 /// 232 /// # Examples 233 /// 234 /// ```no_run 235 /// use ylong_runtime::fs::File; 236 /// use ylong_runtime::io::AsyncWriteExt; 237 /// 238 /// async fn sync_data() -> std::io::Result<()> { 239 /// let mut f = File::create("foo.txt").await?; 240 /// f.write_all(b"Hello, world!").await?; 241 /// 242 /// f.sync_data().await?; 243 /// Ok(()) 244 /// } 245 /// ``` sync_data(&self) -> io::Result<()>246 pub async fn sync_data(&self) -> io::Result<()> { 247 let mut file = self.inner.lock().await; 248 file.flush().await; 249 let file = self.file.clone(); 250 async_op(move || file.sync_data()).await 251 } 252 253 /// Truncates or extends the underlying file, updating the size of this file 254 /// to become size. If the size is less than the current file's size, 255 /// then the file will be shrunk. If it is greater than the current file's 256 /// size, then the file will be extended to size and have all of the 257 /// intermediate data filled in with 0s. The file's cursor isn't 258 /// changed. In particular, if the cursor was at the end and the file is 259 /// shrunk using this operation, the cursor will now be past the end. 260 /// 261 /// # Errors 262 /// 263 /// This function will return an error if the file is not opened for 264 /// writing. 265 /// 266 /// # Example 267 /// 268 /// ```no_run 269 /// use ylong_runtime::fs::File; 270 /// 271 /// async fn set_len() -> std::io::Result<()> { 272 /// let mut f = File::create("foo.txt").await?; 273 /// f.set_len(10).await?; 274 /// 275 /// Ok(()) 276 /// } 277 /// ``` set_len(&self, size: u64) -> io::Result<()>278 pub async fn set_len(&self, size: u64) -> io::Result<()> { 279 let mut file = self.inner.lock().await; 280 file.flush().await; 281 282 let mut buf = match file.state { 283 // after each take, buf will be set right back 284 FileState::Idle(ref mut buf) => buf.take().unwrap(), 285 _ => unreachable!(), 286 }; 287 288 let arc_file = self.file.clone(); 289 290 let (buf, res) = spawn_blocking(&TaskBuilder::new(), move || { 291 let res = if buf.remaining() == 0 { 292 (&*arc_file) 293 .seek(SeekFrom::Current(buf.drop_unread())) 294 .and_then(|_| arc_file.set_len(size)) 295 } else { 296 arc_file.set_len(size) 297 } 298 .map(|_| 0); 299 300 (buf, res) 301 }) 302 .await?; 303 304 file.state = FileState::Idle(Some(buf)); 305 306 res.map(|u| file.idx = u) 307 } 308 309 /// Queries metadata about the underlying file asynchronously. 310 /// 311 /// # Examples 312 /// 313 /// ```no_run 314 /// use ylong_runtime::fs::File; 315 /// 316 /// async fn metadata() -> std::io::Result<()> { 317 /// let mut f = File::open("foo.txt").await?; 318 /// let metadata = f.metadata().await?; 319 /// Ok(()) 320 /// } 321 /// ``` metadata(&self) -> io::Result<Metadata>322 pub async fn metadata(&self) -> io::Result<Metadata> { 323 let file = self.file.clone(); 324 async_op(move || file.metadata()).await 325 } 326 327 /// Creates a new File instance that shares the same underlying file handle 328 /// as the existing File instance. Reads, writes, and seeks will affect both 329 /// File instances simultaneously. 330 /// 331 /// # Example 332 /// 333 /// ```no_run 334 /// use ylong_runtime::fs::File; 335 /// 336 /// async fn try_clone() -> std::io::Result<()> { 337 /// let mut f = File::open("foo.txt").await?; 338 /// let file_copy = f.try_clone().await?; 339 /// Ok(()) 340 /// } 341 /// ``` try_clone(&self) -> io::Result<File>342 pub async fn try_clone(&self) -> io::Result<File> { 343 let file = self.file.clone(); 344 let file = async_op(move || file.try_clone()).await?; 345 Ok(Self::new(file)) 346 } 347 348 /// Sets the buffer size limit for [`AsyncRead`] and [`AsyncWrite`] 349 /// operation of this file. 350 /// 351 /// # Examples 352 /// 353 /// ```no_run 354 /// # async fn set_file_buffer_limit() { 355 /// use ylong_runtime::fs::File; 356 /// use ylong_runtime::io::{AsyncReadExt, AsyncWriteExt}; 357 /// let mut file = File::open("foo.txt").await.unwrap(); 358 /// file.set_buffer_size_limit(1024 * 1024 * 2); 359 /// let mut buf = vec![0; 1024 * 1024 * 16]; 360 /// // write the buffer in chunks up to 2MB each 361 /// let _ = file.write_all(&mut buf).await; 362 /// let _ = file.sync_all().await; 363 /// 364 /// let mut file = File::open("foo.txt").await.unwrap(); 365 /// file.set_buffer_size_limit(1024 * 1024 * 2); 366 /// let mut read_buf = vec![]; 367 /// // read the file in chunks up to 2MB each 368 /// let _ = file.read_to_end(&mut read_buf); 369 /// # } 370 /// ``` set_buffer_size_limit(&mut self, buf_size_limit: usize)371 pub fn set_buffer_size_limit(&mut self, buf_size_limit: usize) { 372 self.buf_size_limit = buf_size_limit; 373 } 374 375 /// Takes the ownership of the `File` and turns it into a [`std::fs::File`]. 376 /// Before the transition, any in-flight operations will be completed. 377 /// 378 /// # Examples 379 /// 380 /// ```no_run 381 /// use ylong_runtime::fs::File; 382 /// 383 /// # async fn file_into_std() -> std::io::Result<()> { 384 /// let file = File::open("foo.txt").await?; 385 /// let std = file.into_std().await; 386 /// # Ok(()) 387 /// # } into_std(mut self) -> SyncFile388 pub async fn into_std(mut self) -> SyncFile { 389 let file = self.inner.get_mut(); 390 file.flush().await; 391 Arc::try_unwrap(self.file).expect("into_std Arc::try_unwrap failed") 392 } 393 394 /// Turns the `File` into [`std::fs::File`] immediately without awaiting any 395 /// in-flight operation to complete. 396 /// 397 /// # Errors 398 /// This method wil return an error containing the file if some operation 399 /// is still in-flight 400 /// 401 /// # Examples 402 /// 403 /// ```no_run 404 /// use ylong_runtime::fs::File; 405 /// 406 /// # async fn file_try_into_std() -> std::io::Result<()> { 407 /// let file = File::open("foo.txt").await?; 408 /// let std = file.try_into_std().unwrap(); 409 /// # Ok(()) 410 /// # } try_into_std(mut self) -> Result<SyncFile, Self>411 pub fn try_into_std(mut self) -> Result<SyncFile, Self> { 412 match Arc::try_unwrap(self.file) { 413 Ok(file) => Ok(file), 414 Err(arc_file) => { 415 self.file = arc_file; 416 Err(self) 417 } 418 } 419 } 420 } 421 422 impl From<SyncFile> for File { from(file: SyncFile) -> Self423 fn from(file: SyncFile) -> Self { 424 Self::new(file) 425 } 426 } 427 428 cfg_unix! { 429 use std::os::unix::io::{AsFd, AsRawFd, FromRawFd, BorrowedFd, RawFd}; 430 431 impl AsRawFd for File { 432 fn as_raw_fd(&self) -> RawFd { 433 self.file.as_raw_fd() 434 } 435 } 436 437 impl AsFd for File { 438 fn as_fd(&self) -> BorrowedFd<'_> { 439 unsafe { BorrowedFd::borrow_raw(self.as_raw_fd()) } 440 } 441 } 442 443 impl FromRawFd for File { 444 unsafe fn from_raw_fd(fd: RawFd) -> Self { 445 SyncFile::from_raw_fd(fd).into() 446 } 447 } 448 } 449 450 cfg_windows! { 451 use std::os::windows::io::{AsRawHandle, FromRawHandle, RawHandle, AsHandle, BorrowedHandle}; 452 453 impl AsRawHandle for File { 454 fn as_raw_handle(&self) -> RawHandle { 455 self.file.as_raw_handle() 456 } 457 } 458 459 impl AsHandle for File { 460 fn as_handle(&self) -> BorrowedHandle<'_> { 461 unsafe { BorrowedHandle::borrow_raw(self.as_raw_handle()) } 462 } 463 } 464 465 impl FromRawHandle for File { 466 unsafe fn from_raw_handle(handle: RawHandle) -> Self { 467 SyncFile::from_raw_handle(handle).into() 468 } 469 } 470 } 471 472 impl AsyncSeek for File { poll_seek( self: Pin<&mut Self>, cx: &mut Context<'_>, mut pos: SeekFrom, ) -> Poll<io::Result<u64>>473 fn poll_seek( 474 self: Pin<&mut Self>, 475 cx: &mut Context<'_>, 476 mut pos: SeekFrom, 477 ) -> Poll<io::Result<u64>> { 478 let file = self.get_mut(); 479 let inner = file.inner.get_mut(); 480 loop { 481 match inner.state { 482 FileState::Idle(ref mut buf) => { 483 // after each take, buf will be set right back 484 let mut r_buf = buf.take().unwrap(); 485 486 // move the cursor back since there's unread data in the buf 487 let unread = r_buf.drop_unread(); 488 if unread != 0 { 489 if let SeekFrom::Current(ref mut idx) = pos { 490 *idx -= unread 491 } 492 } 493 494 let file = file.file.clone(); 495 inner.state = 496 FileState::Seeking(spawn_blocking(&TaskBuilder::new(), move || { 497 let ret = (&*file).seek(pos); 498 (r_buf, ret) 499 })); 500 } 501 ref mut state => { 502 let op = state.get_op(); 503 let res = poll_ready!(Pin::new(state).poll(cx)); 504 match op { 505 FileOp::Reading => {} 506 FileOp::Writing => { 507 if let Err(e) = res { 508 // Save the error for the next write. 509 inner.write_err = Some(e.kind()); 510 } 511 } 512 FileOp::Seeking => { 513 if let Ok(idx) = res { 514 inner.idx = idx; 515 } 516 return Poll::Ready(res); 517 } 518 } 519 } 520 } 521 } 522 } 523 } 524 525 impl AsyncRead for File { poll_read( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>>526 fn poll_read( 527 self: Pin<&mut Self>, 528 cx: &mut Context<'_>, 529 buf: &mut ReadBuf<'_>, 530 ) -> Poll<io::Result<()>> { 531 let file = self.get_mut(); 532 let inner = file.inner.get_mut(); 533 534 loop { 535 match inner.state { 536 FileState::Idle(ref mut file_buf) => { 537 // after each take, buf will be set right back 538 let mut r_buf = file_buf.take().unwrap(); 539 // There is still remaining data from the last read, append it to read buf 540 // directly 541 if r_buf.remaining() != 0 { 542 r_buf.append_to(buf); 543 *file_buf = Some(r_buf); 544 return Poll::Ready(Ok(())); 545 } 546 547 // Make sure there is enough space to read. File_buf's size might be bigger than 548 // the read_buf's size since other thread might also read into the read_buf. 549 r_buf.reserve(buf.remaining(), file.buf_size_limit); 550 551 // State transition 552 let file = file.file.clone(); 553 inner.state = 554 FileState::Reading(spawn_blocking(&TaskBuilder::new(), move || { 555 let ret = r_buf.read(&mut &*file).map(|_| ()); 556 (r_buf, ret) 557 })); 558 } 559 FileState::Reading(ref mut x) => { 560 let (mut file_buf, res) = poll_ready!(Pin::new(x).poll(cx))?; 561 // Append the data inside the file to the read buffer 562 if res.is_ok() { 563 file_buf.append_to(buf); 564 } 565 inner.state = FileState::Idle(Some(file_buf)); 566 return Poll::Ready(res); 567 } 568 FileState::Writing(ref mut x) => { 569 let (file_buf, res) = poll_ready!(Pin::new(x).poll(cx))?; 570 571 // Save the error for the next write 572 if let Err(e) = res { 573 inner.write_err = Some(e.kind()); 574 } 575 inner.state = FileState::Idle(Some(file_buf)) 576 } 577 FileState::Seeking(ref mut x) => { 578 let (file_buf, res) = poll_ready!(Pin::new(x).poll(cx))?; 579 inner.state = FileState::Idle(Some(file_buf)); 580 if let Ok(idx) = res { 581 inner.idx = idx; 582 } 583 } 584 } 585 } 586 } 587 } 588 589 impl AsyncWrite for File { poll_write( self: Pin<&mut Self>, cx: &mut Context<'_>, buf: &[u8], ) -> Poll<io::Result<usize>>590 fn poll_write( 591 self: Pin<&mut Self>, 592 cx: &mut Context<'_>, 593 buf: &[u8], 594 ) -> Poll<io::Result<usize>> { 595 let file = self.get_mut(); 596 let inner = file.inner.get_mut(); 597 598 if let Some(e) = inner.write_err { 599 return Poll::Ready(Err(e.into())); 600 } 601 602 loop { 603 match inner.state { 604 FileState::Idle(ref mut file_buf) => { 605 // after each take, buf will be set right back 606 let mut w_buf = file_buf.take().unwrap(); 607 608 let unread = w_buf.drop_unread(); 609 let n = w_buf.append(buf, file.buf_size_limit); 610 let file = file.file.clone(); 611 612 inner.state = 613 FileState::Writing(spawn_blocking(&TaskBuilder::new(), move || { 614 // Move the cursor back since there's unread data in the buf. 615 if unread != 0 { 616 if let Err(e) = (&*file).seek(SeekFrom::Current(-unread)) { 617 return (w_buf, Err(e)); 618 } 619 } 620 let res = w_buf.write(&mut &*file); 621 (w_buf, res) 622 })); 623 return Poll::Ready(Ok(n)); 624 } 625 ref mut state => { 626 let op = state.get_op(); 627 if let Poll::Ready(Err(e)) = Pin::new(state).poll(cx) { 628 if let FileOp::Writing = op { 629 return Poll::Ready(Err(e)); 630 } 631 } 632 } 633 } 634 } 635 } 636 poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>637 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { 638 self.inner.get_mut().poll_flush(cx) 639 } 640 poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>>641 fn poll_shutdown(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> { 642 self.poll_flush(cx) 643 } 644 } 645 646 impl FileInner { flush(&mut self)647 async fn flush(&mut self) { 648 if let Err(e) = poll_fn(|cx| self.poll_flush(cx)).await { 649 self.write_err = Some(e.kind()); 650 } 651 } 652 poll_flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>>653 fn poll_flush(&mut self, cx: &mut Context) -> Poll<io::Result<()>> { 654 if let Some(e) = self.write_err { 655 return Poll::Ready(Err(e.into())); 656 } 657 658 match self.state { 659 FileState::Idle(_) => Poll::Ready(Ok(())), 660 ref mut state => { 661 let op = state.get_op(); 662 let res = poll_ready!(Pin::new(state).poll(cx)); 663 664 match op { 665 FileOp::Writing => Poll::Ready(res.map(|_| ())), 666 _ => Poll::Ready(Ok(())), 667 } 668 } 669 } 670 } 671 } 672 673 #[cfg(test)] 674 mod test { 675 use std::io; 676 use std::io::SeekFrom; 677 678 use crate::fs::async_file::DEFAULT_BUF_LIMIT; 679 use crate::fs::{remove_file, File, OpenOptions}; 680 use crate::io::{AsyncReadExt, AsyncSeekExt, AsyncWriteExt}; 681 682 /// UT test for `set_len` 683 /// 684 /// # Brief 685 /// 686 /// 1. Creates a new file. 687 /// 2. Removes the file with `set_len()`, check result is Ok(()). 688 /// 3. Deletes the file. 689 #[test] ut_fs_file_set_len()690 fn ut_fs_file_set_len() { 691 crate::block_on(async { 692 let file_path = "file11.txt"; 693 694 let file = File::create(file_path).await.unwrap(); 695 let res = file.set_len(10).await; 696 assert!(res.is_ok()); 697 698 let res = remove_file(file_path).await; 699 assert!(res.is_ok()); 700 }); 701 } 702 703 /// UT test for `try_clone` 704 /// 705 /// # Brief 706 /// 707 /// 1. Creates a new file. 708 /// 2. Creates a new File instance with `try_clone()`, check result is 709 /// Ok(()). 710 /// 3. Deletes the file. 711 #[test] ut_fs_file_try_clone()712 fn ut_fs_file_try_clone() { 713 crate::block_on(async { 714 let file_path = "file12.txt"; 715 716 let file = File::create(file_path).await.unwrap(); 717 let res = file.try_clone().await; 718 assert!(res.is_ok()); 719 720 let res = remove_file(file_path).await; 721 assert!(res.is_ok()); 722 }); 723 } 724 725 /// UT test cases for asynchronous file Seek 726 /// 727 /// # Brief 728 /// 1. Generate an asynchronous file IO with create. 729 /// 2. Start a task to perform a write operation. 730 /// 3. Start another task for seek and read operations. 731 #[test] ut_fs_file_seek()732 fn ut_fs_file_seek() { 733 let file_path = "file13.txt"; 734 let handle = crate::spawn(async move { 735 let mut file = File::create(file_path).await.unwrap(); 736 let buf = vec![65, 66, 67, 68, 69, 70, 71, 72, 73]; 737 let res = file.write(&buf).await.unwrap(); 738 assert_eq!(res, 9); 739 file.sync_all().await.unwrap(); 740 }); 741 crate::block_on(handle).unwrap(); 742 743 let handle2 = crate::spawn(async move { 744 let mut file = File::open(file_path).await.unwrap(); 745 let ret = file.seek(SeekFrom::Current(3)).await.unwrap(); 746 assert_eq!(ret, 3); 747 748 let mut buf = [0; 1]; 749 let ret = file.read(&mut buf).await.unwrap(); 750 assert_eq!(ret, 1); 751 assert_eq!(buf, [68]); 752 753 let ret = file.seek(SeekFrom::Current(1)).await.unwrap(); 754 assert_eq!(ret, 5); 755 756 let mut buf = [0; 1]; 757 let ret = file.read(&mut buf).await.unwrap(); 758 assert_eq!(ret, 1); 759 assert_eq!(buf, [70]); 760 761 let ret = file.seek(SeekFrom::Current(2)).await.unwrap(); 762 assert_eq!(ret, 8); 763 764 let mut buf = [0; 2]; 765 let ret = file.read(&mut buf).await.unwrap(); 766 assert_eq!(ret, 1); 767 assert_eq!(buf, [73, 0]); 768 769 let ret = file.seek(SeekFrom::Start(0)).await.unwrap(); 770 assert_eq!(ret, 0); 771 let mut buf = [0; 9]; 772 let ret = file.read(&mut buf).await.unwrap(); 773 assert_eq!(ret, 9); 774 assert_eq!(buf, [65, 66, 67, 68, 69, 70, 71, 72, 73]); 775 776 let ret = file.seek(SeekFrom::End(-1)).await.unwrap(); 777 assert_eq!(ret, 8); 778 let mut buf = [0; 2]; 779 let ret = file.read(&mut buf).await.unwrap(); 780 assert_eq!(ret, 1); 781 assert_eq!(buf, [73, 0]); 782 }); 783 784 crate::block_on(handle2).unwrap(); 785 std::fs::remove_file(file_path).unwrap(); 786 } 787 788 /// UT test cases for Asynchronous file set permission 789 /// 790 /// # Brief 791 /// 1. Generate an asynchronous file IO with create. 792 /// 2. Asynchronously get the permissions of the file. 793 /// 3. Change the permission to read only, set it to this file. 794 #[test] ut_fs_file_set_permission()795 fn ut_fs_file_set_permission() { 796 let file_path = "file14.txt"; 797 let handle = crate::spawn(async move { 798 let file = File::create(file_path).await.unwrap(); 799 let mut perms = file.metadata().await.unwrap().permissions(); 800 perms.set_readonly(true); 801 let ret = file.set_permissions(perms).await; 802 assert!(ret.is_ok()); 803 let mut perms = file.metadata().await.unwrap().permissions(); 804 #[allow(clippy::permissions_set_readonly_false)] 805 perms.set_readonly(false); 806 let ret = file.set_permissions(perms).await; 807 assert!(ret.is_ok()); 808 }); 809 crate::block_on(handle).unwrap(); 810 std::fs::remove_file(file_path).unwrap(); 811 } 812 813 /// UT test cases for asynchronous file sync 814 /// 815 /// # Brief 816 /// 1. Generate an asynchronous file IO with create. 817 /// 2. Call sync_all and sync_data after asynchronous write. 818 #[test] ut_fs_file_sync_all()819 fn ut_fs_file_sync_all() { 820 let file_path = "file15.txt"; 821 let handle = crate::spawn(async move { 822 let mut file = File::create(file_path).await.unwrap(); 823 let buf = [2; 20000]; 824 let ret = file.write_all(&buf).await; 825 assert!(ret.is_ok()); 826 let ret = file.sync_all().await; 827 assert!(ret.is_ok()); 828 829 let buf = [2; 20000]; 830 let ret = file.write_all(&buf).await; 831 assert!(ret.is_ok()); 832 let ret = file.sync_data().await; 833 assert!(ret.is_ok()); 834 }); 835 crate::block_on(handle).unwrap(); 836 std::fs::remove_file(file_path).unwrap(); 837 } 838 839 /// UT for setting the buffer's size limit 840 /// 841 /// # Brief 842 /// 1. Creates a new file, checks if its buffer size limit equals to 843 /// DEFAULT_BUF_LIMIT 2. Sets a new limit for file buffer 844 /// 3. Writes the entire buffer into the file 845 #[test] ut_fs_set_buf_size_limit()846 fn ut_fs_set_buf_size_limit() { 847 let file_path = "file16.txt"; 848 let handle = crate::spawn(async move { 849 let mut file = File::create(file_path).await.unwrap(); 850 assert_eq!(file.buf_size_limit, DEFAULT_BUF_LIMIT); 851 852 file.set_buffer_size_limit(1024 * 1024 * 4); 853 assert_eq!(file.buf_size_limit, 1024 * 1024 * 4); 854 855 let buf = [2; 20000]; 856 let ret = file.write_all(&buf).await; 857 assert!(ret.is_ok()); 858 let ret = file.sync_all().await; 859 assert!(ret.is_ok()); 860 }); 861 crate::block_on(handle).unwrap(); 862 std::fs::remove_file(file_path).unwrap(); 863 } 864 865 /// UT for opening an non-existed file 866 /// 867 /// # Brief 868 /// 1. Open a file that does not exist 869 /// 2. Check if the returned error is NotFound 870 #[test] ut_fs_open_fail()871 fn ut_fs_open_fail() { 872 let handle = crate::spawn(async move { 873 let file = File::open("file_not_exist").await; 874 assert_eq!(file.unwrap_err().kind(), io::ErrorKind::NotFound); 875 }); 876 crate::block_on(handle).unwrap(); 877 } 878 879 /// UT for `into_std` 880 /// 881 /// # Brief 882 /// 1. Creates an async File 883 /// 2. Turns the async File into a std File 884 /// 3. Check if the std file functions correctly 885 #[test] ut_file_into_std()886 fn ut_file_into_std() { 887 use std::io::{Read, Seek}; 888 889 let file_path = "file17.txt"; 890 let handle = crate::spawn(async move { 891 let mut file = OpenOptions::new() 892 .read(true) 893 .write(true) 894 .create(true) 895 .open(file_path) 896 .await 897 .unwrap(); 898 899 let res = file.write_all(b"hello").await; 900 assert!(res.is_ok()); 901 902 let mut std = file.into_std().await; 903 let ret = std.seek(SeekFrom::Start(0)).unwrap(); 904 assert_eq!(ret, 0); 905 let mut buf = [0; 5]; 906 let ret = std.read_exact(&mut buf); 907 assert!(ret.is_ok()); 908 assert_eq!(&buf, b"hello"); 909 }); 910 crate::block_on(handle).unwrap(); 911 std::fs::remove_file(file_path).unwrap(); 912 } 913 914 /// UT for `try_into_std` 915 /// 916 /// # Brief 917 /// 1. Creates an async File 918 /// 2. Uses the async file to write without flushing 919 /// 3. Tries to turn the async file into a std file 920 /// 4. Check if the attempt fails 921 /// 5. Flushes the file 922 /// 6. Tries to turn the async file into a std file 923 /// 7. Check if the attempt succeeds 924 #[test] ut_file_try_into_std()925 fn ut_file_try_into_std() { 926 let file_path = "file18.txt"; 927 let handle = crate::spawn(async move { 928 let mut file = File::create(file_path).await.unwrap(); 929 let res = file.write_all(b"hello").await; 930 assert!(res.is_ok()); 931 932 let std = file.try_into_std(); 933 assert!(std.is_err()); 934 935 let mut file = std.unwrap_err(); 936 let ret = file.flush().await; 937 assert!(ret.is_ok()); 938 939 let std = file.try_into_std(); 940 assert!(std.is_ok()); 941 }); 942 crate::block_on(handle).unwrap(); 943 std::fs::remove_file(file_path).unwrap(); 944 } 945 946 /// UT for `as_raw_fd` 947 /// 948 /// # Brief 949 /// 1. Creates an async File and turns it into a std file 950 /// 2. Gets the raw fd from the std file 951 /// 3. Turns the std file back to an async file 952 /// 4. Check if the fds are equal 953 /// 5. Creates a new async file from the raw fd 954 /// 6. Check if the fd of the new async file is correct 955 #[test] 956 #[cfg(unix)] ut_file_as_raw_fd()957 fn ut_file_as_raw_fd() { 958 use std::os::fd::{AsFd, AsRawFd}; 959 use std::os::unix::io::FromRawFd; 960 961 let file_path = "file19.txt"; 962 let handle = crate::spawn(async move { 963 let file = File::create(file_path).await.unwrap(); 964 let std = file.into_std().await; 965 let fd = std.as_raw_fd(); 966 let file = File::from(std); 967 let fd2 = file.as_raw_fd(); 968 assert_eq!(fd, fd2); 969 let fd3 = file.as_fd().as_raw_fd(); 970 assert_eq!(fd, fd3); 971 972 let file2 = unsafe { File::from_raw_fd(fd) }; 973 let fd4 = file2.as_raw_fd(); 974 assert_eq!(fd, fd4); 975 }); 976 977 crate::block_on(handle).unwrap(); 978 std::fs::remove_file(file_path).unwrap(); 979 } 980 } 981