//! Unix-specific extensions to primitives in the [`std::fs`] module. //! //! [`std::fs`]: crate::fs #![stable(feature = "rust1", since = "1.0.0")] use super::platform::fs::MetadataExt as _; use crate::fs::{self, OpenOptions, Permissions}; use crate::io; use crate::os::unix::io::{AsFd, AsRawFd}; use crate::path::Path; use crate::sys; use crate::sys_common::{AsInner, AsInnerMut, FromInner}; // Used for `File::read` on intra-doc links use crate::ffi::OsStr; use crate::sealed::Sealed; #[allow(unused_imports)] use io::{Read, Write}; // Tests for this module #[cfg(test)] mod tests; /// Unix-specific extensions to [`fs::File`]. #[stable(feature = "file_offset", since = "1.15.0")] pub trait FileExt { /// Reads a number of bytes starting from a given offset. /// /// Returns the number of bytes read. /// /// The offset is relative to the start of the file and thus independent /// from the current cursor. /// /// The current file cursor is not affected by this function. /// /// Note that similar to [`File::read`], it is not an error to return with a /// short read. /// /// [`File::read`]: fs::File::read /// /// # Examples /// /// ```no_run /// use std::io; /// use std::fs::File; /// use std::os::unix::prelude::FileExt; /// /// fn main() -> io::Result<()> { /// let mut buf = [0u8; 8]; /// let file = File::open("foo.txt")?; /// /// // We now read 8 bytes from the offset 10. /// let num_bytes_read = file.read_at(&mut buf, 10)?; /// println!("read {num_bytes_read} bytes: {buf:?}"); /// Ok(()) /// } /// ``` #[stable(feature = "file_offset", since = "1.15.0")] fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result; /// Like `read_at`, except that it reads into a slice of buffers. /// /// Data is copied to fill each buffer in order, with the final buffer /// written to possibly being only partially filled. This method must behave /// equivalently to a single call to read with concatenated buffers. #[unstable(feature = "unix_file_vectored_at", issue = "89517")] fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result { io::default_read_vectored(|b| self.read_at(b, offset), bufs) } /// Reads the exact number of byte required to fill `buf` from the given offset. /// /// The offset is relative to the start of the file and thus independent /// from the current cursor. /// /// The current file cursor is not affected by this function. /// /// Similar to [`io::Read::read_exact`] but uses [`read_at`] instead of `read`. /// /// [`read_at`]: FileExt::read_at /// /// # Errors /// /// If this function encounters an error of the kind /// [`io::ErrorKind::Interrupted`] then the error is ignored and the operation /// will continue. /// /// If this function encounters an "end of file" before completely filling /// the buffer, it returns an error of the kind [`io::ErrorKind::UnexpectedEof`]. /// The contents of `buf` are unspecified in this case. /// /// If any other read error is encountered then this function immediately /// returns. The contents of `buf` are unspecified in this case. /// /// If this function returns an error, it is unspecified how many bytes it /// has read, but it will never read more than would be necessary to /// completely fill the buffer. /// /// # Examples /// /// ```no_run /// use std::io; /// use std::fs::File; /// use std::os::unix::prelude::FileExt; /// /// fn main() -> io::Result<()> { /// let mut buf = [0u8; 8]; /// let file = File::open("foo.txt")?; /// /// // We now read exactly 8 bytes from the offset 10. /// file.read_exact_at(&mut buf, 10)?; /// println!("read {} bytes: {:?}", buf.len(), buf); /// Ok(()) /// } /// ``` #[stable(feature = "rw_exact_all_at", since = "1.33.0")] fn read_exact_at(&self, mut buf: &mut [u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.read_at(buf, offset) { Ok(0) => break, Ok(n) => { let tmp = buf; buf = &mut tmp[n..]; offset += n as u64; } Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } if !buf.is_empty() { Err(io::const_io_error!(io::ErrorKind::UnexpectedEof, "failed to fill whole buffer",)) } else { Ok(()) } } /// Writes a number of bytes starting from a given offset. /// /// Returns the number of bytes written. /// /// The offset is relative to the start of the file and thus independent /// from the current cursor. /// /// The current file cursor is not affected by this function. /// /// When writing beyond the end of the file, the file is appropriately /// extended and the intermediate bytes are initialized with the value 0. /// /// Note that similar to [`File::write`], it is not an error to return a /// short write. /// /// # Bug /// On some systems, `write_at` utilises [`pwrite64`] to write to files. /// However, this syscall has a [bug] where files opened with the `O_APPEND` /// flag fail to respect the offset parameter, always appending to the end /// of the file instead. /// /// It is possible to inadvertently set this flag, like in the example below. /// Therefore, it is important to be vigilant while changing options to mitigate /// unexpected behaviour. /// /// ```no_run /// use std::fs::File; /// use std::io; /// use std::os::unix::prelude::FileExt; /// /// fn main() -> io::Result<()> { /// // Open a file with the append option (sets the `O_APPEND` flag) /// let file = File::options().append(true).open("foo.txt")?; /// /// // We attempt to write at offset 10; instead appended to EOF /// file.write_at(b"sushi", 10)?; /// /// // foo.txt is 5 bytes long instead of 15 /// Ok(()) /// } /// ``` /// /// [`File::write`]: fs::File::write /// [`pwrite64`]: https://man7.org/linux/man-pages/man2/pwrite.2.html /// [bug]: https://man7.org/linux/man-pages/man2/pwrite.2.html#BUGS /// /// # Examples /// /// ```no_run /// use std::fs::File; /// use std::io; /// use std::os::unix::prelude::FileExt; /// /// fn main() -> io::Result<()> { /// let file = File::create("foo.txt")?; /// /// // We now write at the offset 10. /// file.write_at(b"sushi", 10)?; /// Ok(()) /// } /// ``` #[stable(feature = "file_offset", since = "1.15.0")] fn write_at(&self, buf: &[u8], offset: u64) -> io::Result; /// Like `write_at`, except that it writes from a slice of buffers. /// /// Data is copied from each buffer in order, with the final buffer read /// from possibly being only partially consumed. This method must behave as /// a call to `write_at` with the buffers concatenated would. #[unstable(feature = "unix_file_vectored_at", issue = "89517")] fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result { io::default_write_vectored(|b| self.write_at(b, offset), bufs) } /// Attempts to write an entire buffer starting from a given offset. /// /// The offset is relative to the start of the file and thus independent /// from the current cursor. /// /// The current file cursor is not affected by this function. /// /// This method will continuously call [`write_at`] until there is no more data /// to be written or an error of non-[`io::ErrorKind::Interrupted`] kind is /// returned. This method will not return until the entire buffer has been /// successfully written or such an error occurs. The first error that is /// not of [`io::ErrorKind::Interrupted`] kind generated from this method will be /// returned. /// /// # Errors /// /// This function will return the first error of /// non-[`io::ErrorKind::Interrupted`] kind that [`write_at`] returns. /// /// [`write_at`]: FileExt::write_at /// /// # Examples /// /// ```no_run /// use std::fs::File; /// use std::io; /// use std::os::unix::prelude::FileExt; /// /// fn main() -> io::Result<()> { /// let file = File::open("foo.txt")?; /// /// // We now write at the offset 10. /// file.write_all_at(b"sushi", 10)?; /// Ok(()) /// } /// ``` #[stable(feature = "rw_exact_all_at", since = "1.33.0")] fn write_all_at(&self, mut buf: &[u8], mut offset: u64) -> io::Result<()> { while !buf.is_empty() { match self.write_at(buf, offset) { Ok(0) => { return Err(io::const_io_error!( io::ErrorKind::WriteZero, "failed to write whole buffer", )); } Ok(n) => { buf = &buf[n..]; offset += n as u64 } Err(ref e) if e.is_interrupted() => {} Err(e) => return Err(e), } } Ok(()) } } #[stable(feature = "file_offset", since = "1.15.0")] impl FileExt for fs::File { fn read_at(&self, buf: &mut [u8], offset: u64) -> io::Result { self.as_inner().read_at(buf, offset) } fn read_vectored_at(&self, bufs: &mut [io::IoSliceMut<'_>], offset: u64) -> io::Result { self.as_inner().read_vectored_at(bufs, offset) } fn write_at(&self, buf: &[u8], offset: u64) -> io::Result { self.as_inner().write_at(buf, offset) } fn write_vectored_at(&self, bufs: &[io::IoSlice<'_>], offset: u64) -> io::Result { self.as_inner().write_vectored_at(bufs, offset) } } /// Unix-specific extensions to [`fs::Permissions`]. #[stable(feature = "fs_ext", since = "1.1.0")] pub trait PermissionsExt { /// Returns the underlying raw `st_mode` bits that contain the standard /// Unix permissions for this file. /// /// # Examples /// /// ```no_run /// use std::fs::File; /// use std::os::unix::fs::PermissionsExt; /// /// fn main() -> std::io::Result<()> { /// let f = File::create("foo.txt")?; /// let metadata = f.metadata()?; /// let permissions = metadata.permissions(); /// /// println!("permissions: {:o}", permissions.mode()); /// Ok(()) /// } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn mode(&self) -> u32; /// Sets the underlying raw bits for this set of permissions. /// /// # Examples /// /// ```no_run /// use std::fs::File; /// use std::os::unix::fs::PermissionsExt; /// /// fn main() -> std::io::Result<()> { /// let f = File::create("foo.txt")?; /// let metadata = f.metadata()?; /// let mut permissions = metadata.permissions(); /// /// permissions.set_mode(0o644); // Read/write for owner and read for others. /// assert_eq!(permissions.mode(), 0o644); /// Ok(()) /// } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn set_mode(&mut self, mode: u32); /// Creates a new instance of `Permissions` from the given set of Unix /// permission bits. /// /// # Examples /// /// ``` /// use std::fs::Permissions; /// use std::os::unix::fs::PermissionsExt; /// /// // Read/write for owner and read for others. /// let permissions = Permissions::from_mode(0o644); /// assert_eq!(permissions.mode(), 0o644); /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn from_mode(mode: u32) -> Self; } #[stable(feature = "fs_ext", since = "1.1.0")] impl PermissionsExt for Permissions { fn mode(&self) -> u32 { self.as_inner().mode() } fn set_mode(&mut self, mode: u32) { *self = Permissions::from_inner(FromInner::from_inner(mode)); } fn from_mode(mode: u32) -> Permissions { Permissions::from_inner(FromInner::from_inner(mode)) } } /// Unix-specific extensions to [`fs::OpenOptions`]. #[stable(feature = "fs_ext", since = "1.1.0")] pub trait OpenOptionsExt { /// Sets the mode bits that a new file will be created with. /// /// If a new file is created as part of an `OpenOptions::open` call then this /// specified `mode` will be used as the permission bits for the new file. /// If no `mode` is set, the default of `0o666` will be used. /// The operating system masks out bits with the system's `umask`, to produce /// the final permissions. /// /// # Examples /// /// ```no_run /// use std::fs::OpenOptions; /// use std::os::unix::fs::OpenOptionsExt; /// /// # fn main() { /// let mut options = OpenOptions::new(); /// options.mode(0o644); // Give read/write for owner and read for others. /// let file = options.open("foo.txt"); /// # } /// ``` #[stable(feature = "fs_ext", since = "1.1.0")] fn mode(&mut self, mode: u32) -> &mut Self; /// Pass custom flags to the `flags` argument of `open`. /// /// The bits that define the access mode are masked out with `O_ACCMODE`, to /// ensure they do not interfere with the access mode set by Rusts options. /// /// Custom flags can only set flags, not remove flags set by Rusts options. /// This options overwrites any previously set custom flags. /// /// # Examples /// /// ```no_run /// # #![feature(rustc_private)] /// use libc; /// use std::fs::OpenOptions; /// use std::os::unix::fs::OpenOptionsExt; /// /// # fn main() { /// let mut options = OpenOptions::new(); /// options.write(true); /// if cfg!(unix) { /// options.custom_flags(libc::O_NOFOLLOW); /// } /// let file = options.open("foo.txt"); /// # } /// ``` #[stable(feature = "open_options_ext", since = "1.10.0")] fn custom_flags(&mut self, flags: i32) -> &mut Self; } #[stable(feature = "fs_ext", since = "1.1.0")] impl OpenOptionsExt for OpenOptions { fn mode(&mut self, mode: u32) -> &mut OpenOptions { self.as_inner_mut().mode(mode); self } fn custom_flags(&mut self, flags: i32) -> &mut OpenOptions { self.as_inner_mut().custom_flags(flags); self } } /// Unix-specific extensions to [`fs::Metadata`]. #[stable(feature = "metadata_ext", since = "1.1.0")] pub trait MetadataExt { /// Returns the ID of the device containing the file. /// /// # Examples /// /// ```no_run /// use std::io; /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let dev_id = meta.dev(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn dev(&self) -> u64; /// Returns the inode number. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let inode = meta.ino(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn ino(&self) -> u64; /// Returns the rights applied to this file. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let mode = meta.mode(); /// let user_has_write_access = mode & 0o200; /// let user_has_read_write_access = mode & 0o600; /// let group_has_read_access = mode & 0o040; /// let others_have_exec_access = mode & 0o001; /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn mode(&self) -> u32; /// Returns the number of hard links pointing to this file. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let nb_hard_links = meta.nlink(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn nlink(&self) -> u64; /// Returns the user ID of the owner of this file. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let user_id = meta.uid(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn uid(&self) -> u32; /// Returns the group ID of the owner of this file. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let group_id = meta.gid(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn gid(&self) -> u32; /// Returns the device ID of this file (if it is a special one). /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let device_id = meta.rdev(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn rdev(&self) -> u64; /// Returns the total size of this file in bytes. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let file_size = meta.size(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn size(&self) -> u64; /// Returns the last access time of the file, in seconds since Unix Epoch. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let last_access_time = meta.atime(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn atime(&self) -> i64; /// Returns the last access time of the file, in nanoseconds since [`atime`]. /// /// [`atime`]: MetadataExt::atime /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let nano_last_access_time = meta.atime_nsec(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn atime_nsec(&self) -> i64; /// Returns the last modification time of the file, in seconds since Unix Epoch. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let last_modification_time = meta.mtime(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn mtime(&self) -> i64; /// Returns the last modification time of the file, in nanoseconds since [`mtime`]. /// /// [`mtime`]: MetadataExt::mtime /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let nano_last_modification_time = meta.mtime_nsec(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn mtime_nsec(&self) -> i64; /// Returns the last status change time of the file, in seconds since Unix Epoch. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let last_status_change_time = meta.ctime(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn ctime(&self) -> i64; /// Returns the last status change time of the file, in nanoseconds since [`ctime`]. /// /// [`ctime`]: MetadataExt::ctime /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let nano_last_status_change_time = meta.ctime_nsec(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn ctime_nsec(&self) -> i64; /// Returns the block size for filesystem I/O. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let block_size = meta.blksize(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn blksize(&self) -> u64; /// Returns the number of blocks allocated to the file, in 512-byte units. /// /// Please note that this may be smaller than `st_size / 512` when the file has holes. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::MetadataExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("some_file")?; /// let blocks = meta.blocks(); /// Ok(()) /// } /// ``` #[stable(feature = "metadata_ext", since = "1.1.0")] fn blocks(&self) -> u64; #[cfg(target_os = "vxworks")] #[stable(feature = "metadata_ext", since = "1.1.0")] fn attrib(&self) -> u8; } #[stable(feature = "metadata_ext", since = "1.1.0")] impl MetadataExt for fs::Metadata { fn dev(&self) -> u64 { self.st_dev() } fn ino(&self) -> u64 { self.st_ino() } fn mode(&self) -> u32 { self.st_mode() } fn nlink(&self) -> u64 { self.st_nlink() } fn uid(&self) -> u32 { self.st_uid() } fn gid(&self) -> u32 { self.st_gid() } fn rdev(&self) -> u64 { self.st_rdev() } fn size(&self) -> u64 { self.st_size() } fn atime(&self) -> i64 { self.st_atime() } fn atime_nsec(&self) -> i64 { self.st_atime_nsec() } fn mtime(&self) -> i64 { self.st_mtime() } fn mtime_nsec(&self) -> i64 { self.st_mtime_nsec() } fn ctime(&self) -> i64 { self.st_ctime() } fn ctime_nsec(&self) -> i64 { self.st_ctime_nsec() } fn blksize(&self) -> u64 { self.st_blksize() } fn blocks(&self) -> u64 { self.st_blocks() } #[cfg(target_os = "vxworks")] fn attrib(&self) -> u8 { self.st_attrib() } } /// Unix-specific extensions for [`fs::FileType`]. /// /// Adds support for special Unix file types such as block/character devices, /// pipes, and sockets. #[stable(feature = "file_type_ext", since = "1.5.0")] pub trait FileTypeExt { /// Returns `true` if this file type is a block device. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("block_device_file")?; /// let file_type = meta.file_type(); /// assert!(file_type.is_block_device()); /// Ok(()) /// } /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_block_device(&self) -> bool; /// Returns `true` if this file type is a char device. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("char_device_file")?; /// let file_type = meta.file_type(); /// assert!(file_type.is_char_device()); /// Ok(()) /// } /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_char_device(&self) -> bool; /// Returns `true` if this file type is a fifo. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("fifo_file")?; /// let file_type = meta.file_type(); /// assert!(file_type.is_fifo()); /// Ok(()) /// } /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_fifo(&self) -> bool; /// Returns `true` if this file type is a socket. /// /// # Examples /// /// ```no_run /// use std::fs; /// use std::os::unix::fs::FileTypeExt; /// use std::io; /// /// fn main() -> io::Result<()> { /// let meta = fs::metadata("unix.socket")?; /// let file_type = meta.file_type(); /// assert!(file_type.is_socket()); /// Ok(()) /// } /// ``` #[stable(feature = "file_type_ext", since = "1.5.0")] fn is_socket(&self) -> bool; } #[stable(feature = "file_type_ext", since = "1.5.0")] impl FileTypeExt for fs::FileType { fn is_block_device(&self) -> bool { self.as_inner().is(libc::S_IFBLK) } fn is_char_device(&self) -> bool { self.as_inner().is(libc::S_IFCHR) } fn is_fifo(&self) -> bool { self.as_inner().is(libc::S_IFIFO) } fn is_socket(&self) -> bool { self.as_inner().is(libc::S_IFSOCK) } } /// Unix-specific extension methods for [`fs::DirEntry`]. #[stable(feature = "dir_entry_ext", since = "1.1.0")] pub trait DirEntryExt { /// Returns the underlying `d_ino` field in the contained `dirent` /// structure. /// /// # Examples /// /// ``` /// use std::fs; /// use std::os::unix::fs::DirEntryExt; /// /// if let Ok(entries) = fs::read_dir(".") { /// for entry in entries { /// if let Ok(entry) = entry { /// // Here, `entry` is a `DirEntry`. /// println!("{:?}: {}", entry.file_name(), entry.ino()); /// } /// } /// } /// ``` #[stable(feature = "dir_entry_ext", since = "1.1.0")] fn ino(&self) -> u64; } #[stable(feature = "dir_entry_ext", since = "1.1.0")] impl DirEntryExt for fs::DirEntry { fn ino(&self) -> u64 { self.as_inner().ino() } } /// Sealed Unix-specific extension methods for [`fs::DirEntry`]. #[unstable(feature = "dir_entry_ext2", issue = "85573")] pub trait DirEntryExt2: Sealed { /// Returns a reference to the underlying `OsStr` of this entry's filename. /// /// # Examples /// /// ``` /// #![feature(dir_entry_ext2)] /// use std::os::unix::fs::DirEntryExt2; /// use std::{fs, io}; /// /// fn main() -> io::Result<()> { /// let mut entries = fs::read_dir(".")?.collect::, io::Error>>()?; /// entries.sort_unstable_by(|a, b| a.file_name_ref().cmp(b.file_name_ref())); /// /// for p in entries { /// println!("{p:?}"); /// } /// /// Ok(()) /// } /// ``` fn file_name_ref(&self) -> &OsStr; } /// Allows extension traits within `std`. #[unstable(feature = "sealed", issue = "none")] impl Sealed for fs::DirEntry {} #[unstable(feature = "dir_entry_ext2", issue = "85573")] impl DirEntryExt2 for fs::DirEntry { fn file_name_ref(&self) -> &OsStr { self.as_inner().file_name_os_str() } } /// Creates a new symbolic link on the filesystem. /// /// The `link` path will be a symbolic link pointing to the `original` path. /// /// # Examples /// /// ```no_run /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { /// fs::symlink("a.txt", "b.txt")?; /// Ok(()) /// } /// ``` #[stable(feature = "symlink", since = "1.1.0")] pub fn symlink, Q: AsRef>(original: P, link: Q) -> io::Result<()> { sys::fs::symlink(original.as_ref(), link.as_ref()) } /// Unix-specific extensions to [`fs::DirBuilder`]. #[stable(feature = "dir_builder", since = "1.6.0")] pub trait DirBuilderExt { /// Sets the mode to create new directories with. This option defaults to /// 0o777. /// /// # Examples /// /// ```no_run /// use std::fs::DirBuilder; /// use std::os::unix::fs::DirBuilderExt; /// /// let mut builder = DirBuilder::new(); /// builder.mode(0o755); /// ``` #[stable(feature = "dir_builder", since = "1.6.0")] fn mode(&mut self, mode: u32) -> &mut Self; } #[stable(feature = "dir_builder", since = "1.6.0")] impl DirBuilderExt for fs::DirBuilder { fn mode(&mut self, mode: u32) -> &mut fs::DirBuilder { self.as_inner_mut().set_mode(mode); self } } /// Change the owner and group of the specified path. /// /// Specifying either the uid or gid as `None` will leave it unchanged. /// /// Changing the owner typically requires privileges, such as root or a specific capability. /// Changing the group typically requires either being the owner and a member of the group, or /// having privileges. /// /// If called on a symbolic link, this will change the owner and group of the link target. To /// change the owner and group of the link itself, see [`lchown`]. /// /// # Examples /// /// ```no_run /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { /// fs::chown("/sandbox", Some(0), Some(0))?; /// Ok(()) /// } /// ``` #[stable(feature = "unix_chown", since = "1.73.0")] pub fn chown>(dir: P, uid: Option, gid: Option) -> io::Result<()> { sys::fs::chown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } /// Change the owner and group of the file referenced by the specified open file descriptor. /// /// For semantics and required privileges, see [`chown`]. /// /// # Examples /// /// ```no_run /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { /// let f = std::fs::File::open("/file")?; /// fs::fchown(&f, Some(0), Some(0))?; /// Ok(()) /// } /// ``` #[stable(feature = "unix_chown", since = "1.73.0")] pub fn fchown(fd: F, uid: Option, gid: Option) -> io::Result<()> { sys::fs::fchown(fd.as_fd().as_raw_fd(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } /// Change the owner and group of the specified path, without dereferencing symbolic links. /// /// Identical to [`chown`], except that if called on a symbolic link, this will change the owner /// and group of the link itself rather than the owner and group of the link target. /// /// # Examples /// /// ```no_run /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { /// fs::lchown("/symlink", Some(0), Some(0))?; /// Ok(()) /// } /// ``` #[stable(feature = "unix_chown", since = "1.73.0")] pub fn lchown>(dir: P, uid: Option, gid: Option) -> io::Result<()> { sys::fs::lchown(dir.as_ref(), uid.unwrap_or(u32::MAX), gid.unwrap_or(u32::MAX)) } /// Change the root directory of the current process to the specified path. /// /// This typically requires privileges, such as root or a specific capability. /// /// This does not change the current working directory; you should call /// [`std::env::set_current_dir`][`crate::env::set_current_dir`] afterwards. /// /// # Examples /// /// ```no_run /// use std::os::unix::fs; /// /// fn main() -> std::io::Result<()> { /// fs::chroot("/sandbox")?; /// std::env::set_current_dir("/")?; /// // continue working in sandbox /// Ok(()) /// } /// ``` #[stable(feature = "unix_chroot", since = "1.56.0")] #[cfg(not(any(target_os = "fuchsia", target_os = "vxworks")))] pub fn chroot>(dir: P) -> io::Result<()> { sys::fs::chroot(dir.as_ref()) }