| 1 | //! Functions which operate on file descriptors. |
| 2 | |
| 3 | #[cfg (not(target_os = "wasi" ))] |
| 4 | use crate::fs::Mode; |
| 5 | #[cfg (not(target_os = "wasi" ))] |
| 6 | use crate::fs::{Gid, Uid}; |
| 7 | use crate::fs::{OFlags, SeekFrom, Timespec}; |
| 8 | use crate::{backend, io}; |
| 9 | use backend::fd::{AsFd, BorrowedFd}; |
| 10 | #[cfg (not(any( |
| 11 | netbsdlike, |
| 12 | target_os = "dragonfly" , |
| 13 | target_os = "espidf" , |
| 14 | target_os = "nto" , |
| 15 | target_os = "redox" , |
| 16 | target_os = "vita" , |
| 17 | )))] |
| 18 | use backend::fs::types::FallocateFlags; |
| 19 | #[cfg (not(any( |
| 20 | target_os = "espidf" , |
| 21 | target_os = "solaris" , |
| 22 | target_os = "vita" , |
| 23 | target_os = "wasi" |
| 24 | )))] |
| 25 | use backend::fs::types::FlockOperation; |
| 26 | #[cfg (linux_kernel)] |
| 27 | use backend::fs::types::FsWord; |
| 28 | use backend::fs::types::Stat; |
| 29 | #[cfg (not(any( |
| 30 | solarish, |
| 31 | target_os = "espidf" , |
| 32 | target_os = "haiku" , |
| 33 | target_os = "netbsd" , |
| 34 | target_os = "nto" , |
| 35 | target_os = "redox" , |
| 36 | target_os = "vita" , |
| 37 | target_os = "wasi" , |
| 38 | )))] |
| 39 | use backend::fs::types::StatFs; |
| 40 | #[cfg (not(any(target_os = "haiku" , target_os = "redox" , target_os = "wasi" )))] |
| 41 | use backend::fs::types::StatVfs; |
| 42 | use core::fmt; |
| 43 | |
| 44 | /// Timestamps used by [`utimensat`] and [`futimens`]. |
| 45 | /// |
| 46 | /// [`utimensat`]: crate::fs::utimensat |
| 47 | /// [`futimens`]: crate::fs::futimens |
| 48 | // This is `repr(C)` and specifically laid out to match the representation used |
| 49 | // by `utimensat` and `futimens`, which expect 2-element arrays of timestamps. |
| 50 | #[repr (C)] |
| 51 | #[derive (Clone)] |
| 52 | pub struct Timestamps { |
| 53 | /// The timestamp of the last access to a filesystem object. |
| 54 | pub last_access: Timespec, |
| 55 | |
| 56 | /// The timestamp of the last modification of a filesystem object. |
| 57 | pub last_modification: Timespec, |
| 58 | } |
| 59 | |
| 60 | impl fmt::Debug for Timestamps { |
| 61 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
| 62 | f&mut DebugStruct<'_, '_>.debug_struct("Timestamps" ) |
| 63 | .field("last_access.tv_sec" , &self.last_access.tv_sec) |
| 64 | .field("last_access.tv_nsec" , &self.last_access.tv_nsec) |
| 65 | .field("last_modification.tv_sec" , &self.last_modification.tv_sec) |
| 66 | .field(name:"last_modification.tv_nsec" , &self.last_modification.tv_nsec) |
| 67 | .finish() |
| 68 | } |
| 69 | } |
| 70 | |
| 71 | /// The filesystem magic number for procfs. |
| 72 | /// |
| 73 | /// See [the `fstatfs` manual page] for more information. |
| 74 | /// |
| 75 | /// [the `fstatfs` manual page]: https://man7.org/linux/man-pages/man2/fstatfs.2.html#DESCRIPTION |
| 76 | #[cfg (linux_kernel)] |
| 77 | pub const PROC_SUPER_MAGIC: FsWord = backend::c::PROC_SUPER_MAGIC as FsWord; |
| 78 | |
| 79 | /// The filesystem magic number for NFS. |
| 80 | /// |
| 81 | /// See [the `fstatfs` manual page] for more information. |
| 82 | /// |
| 83 | /// [the `fstatfs` manual page]: https://man7.org/linux/man-pages/man2/fstatfs.2.html#DESCRIPTION |
| 84 | #[cfg (linux_kernel)] |
| 85 | pub const NFS_SUPER_MAGIC: FsWord = backend::c::NFS_SUPER_MAGIC as FsWord; |
| 86 | |
| 87 | /// `lseek(fd, offset, whence)`—Repositions a file descriptor within a file. |
| 88 | /// |
| 89 | /// # References |
| 90 | /// - [POSIX] |
| 91 | /// - [Linux] |
| 92 | /// |
| 93 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/lseek.html |
| 94 | /// [Linux]: https://man7.org/linux/man-pages/man2/lseek.2.html |
| 95 | #[inline ] |
| 96 | #[doc (alias = "lseek" )] |
| 97 | pub fn seek<Fd: AsFd>(fd: Fd, pos: SeekFrom) -> io::Result<u64> { |
| 98 | backend::fs::syscalls::seek(fd.as_fd(), pos) |
| 99 | } |
| 100 | |
| 101 | /// `lseek(fd, 0, SEEK_CUR)`—Returns the current position within a file. |
| 102 | /// |
| 103 | /// Return the current position of the file descriptor. This is a subset of |
| 104 | /// the functionality of `seek`, but this interface makes it easier for users |
| 105 | /// to declare their intent not to mutate any state. |
| 106 | /// |
| 107 | /// # References |
| 108 | /// - [POSIX] |
| 109 | /// - [Linux] |
| 110 | /// |
| 111 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/lseek.html |
| 112 | /// [Linux]: https://man7.org/linux/man-pages/man2/lseek.2.html |
| 113 | #[inline ] |
| 114 | #[doc (alias = "lseek" )] |
| 115 | pub fn tell<Fd: AsFd>(fd: Fd) -> io::Result<u64> { |
| 116 | backend::fs::syscalls::tell(fd.as_fd()) |
| 117 | } |
| 118 | |
| 119 | /// `fchmod(fd, mode)`—Sets open file or directory permissions. |
| 120 | /// |
| 121 | /// This implementation does not support [`OFlags::PATH`] file descriptors, |
| 122 | /// even on platforms where the host libc emulates it. |
| 123 | /// |
| 124 | /// # References |
| 125 | /// - [POSIX] |
| 126 | /// - [Linux] |
| 127 | /// |
| 128 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchmod.html |
| 129 | /// [Linux]: https://man7.org/linux/man-pages/man2/fchmod.2.html |
| 130 | #[cfg (not(target_os = "wasi" ))] |
| 131 | #[inline ] |
| 132 | pub fn fchmod<Fd: AsFd>(fd: Fd, mode: Mode) -> io::Result<()> { |
| 133 | backend::fs::syscalls::fchmod(fd.as_fd(), mode) |
| 134 | } |
| 135 | |
| 136 | /// `fchown(fd, owner, group)`—Sets open file or directory ownership. |
| 137 | /// |
| 138 | /// # References |
| 139 | /// - [POSIX] |
| 140 | /// - [Linux] |
| 141 | /// |
| 142 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fchown.html |
| 143 | /// [Linux]: https://man7.org/linux/man-pages/man2/fchown.2.html |
| 144 | #[cfg (not(target_os = "wasi" ))] |
| 145 | #[inline ] |
| 146 | pub fn fchown<Fd: AsFd>(fd: Fd, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> { |
| 147 | backend::fs::syscalls::fchown(fd.as_fd(), owner, group) |
| 148 | } |
| 149 | |
| 150 | /// `fstat(fd)`—Queries metadata for an open file or directory. |
| 151 | /// |
| 152 | /// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to |
| 153 | /// interpret the `st_mode` field. |
| 154 | /// |
| 155 | /// # References |
| 156 | /// - [POSIX] |
| 157 | /// - [Linux] |
| 158 | /// |
| 159 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstat.html |
| 160 | /// [Linux]: https://man7.org/linux/man-pages/man2/fstat.2.html |
| 161 | /// [`Mode::from_raw_mode`]: Mode::from_raw_mode |
| 162 | /// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode |
| 163 | #[inline ] |
| 164 | pub fn fstat<Fd: AsFd>(fd: Fd) -> io::Result<Stat> { |
| 165 | backend::fs::syscalls::fstat(fd.as_fd()) |
| 166 | } |
| 167 | |
| 168 | /// `fstatfs(fd)`—Queries filesystem statistics for an open file or directory. |
| 169 | /// |
| 170 | /// Compared to [`fstatvfs`], this function often provides more information, |
| 171 | /// though it's less portable. |
| 172 | /// |
| 173 | /// # References |
| 174 | /// - [Linux] |
| 175 | /// |
| 176 | /// [Linux]: https://man7.org/linux/man-pages/man2/fstatfs.2.html |
| 177 | #[cfg (not(any( |
| 178 | solarish, |
| 179 | target_os = "espidf" , |
| 180 | target_os = "haiku" , |
| 181 | target_os = "netbsd" , |
| 182 | target_os = "nto" , |
| 183 | target_os = "redox" , |
| 184 | target_os = "vita" , |
| 185 | target_os = "wasi" , |
| 186 | )))] |
| 187 | #[inline ] |
| 188 | pub fn fstatfs<Fd: AsFd>(fd: Fd) -> io::Result<StatFs> { |
| 189 | backend::fs::syscalls::fstatfs(fd.as_fd()) |
| 190 | } |
| 191 | |
| 192 | /// `fstatvfs(fd)`—Queries filesystem statistics for an open file or |
| 193 | /// directory, POSIX version. |
| 194 | /// |
| 195 | /// Compared to [`fstatfs`], this function often provides less information, |
| 196 | /// but it is more portable. But even so, filesystems are very diverse and not |
| 197 | /// all the fields are meaningful for every filesystem. And `f_fsid` doesn't |
| 198 | /// seem to have a clear meaning anywhere. |
| 199 | /// |
| 200 | /// # References |
| 201 | /// - [POSIX] |
| 202 | /// - [Linux] |
| 203 | /// |
| 204 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fstatvfs.html |
| 205 | /// [Linux]: https://man7.org/linux/man-pages/man2/fstatvfs.2.html |
| 206 | #[cfg (not(any(target_os = "haiku" , target_os = "redox" , target_os = "wasi" )))] |
| 207 | #[inline ] |
| 208 | pub fn fstatvfs<Fd: AsFd>(fd: Fd) -> io::Result<StatVfs> { |
| 209 | backend::fs::syscalls::fstatvfs(fd.as_fd()) |
| 210 | } |
| 211 | |
| 212 | /// `futimens(fd, times)`—Sets timestamps for an open file or directory. |
| 213 | /// |
| 214 | /// # References |
| 215 | /// - [POSIX] |
| 216 | /// - [Linux] |
| 217 | /// |
| 218 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/futimens.html |
| 219 | /// [Linux]: https://man7.org/linux/man-pages/man2/utimensat.2.html |
| 220 | #[cfg (not(any(target_os = "espidf" , target_os = "vita" )))] |
| 221 | #[inline ] |
| 222 | pub fn futimens<Fd: AsFd>(fd: Fd, times: &Timestamps) -> io::Result<()> { |
| 223 | backend::fs::syscalls::futimens(fd.as_fd(), times) |
| 224 | } |
| 225 | |
| 226 | /// `fallocate(fd, mode, offset, len)`—Adjusts file allocation. |
| 227 | /// |
| 228 | /// This is a more general form of `posix_fallocate`, adding a `mode` argument |
| 229 | /// which modifies the behavior. On platforms which only support |
| 230 | /// `posix_fallocate` and not the more general form, no `FallocateFlags` values |
| 231 | /// are defined so it will always be empty. |
| 232 | /// |
| 233 | /// # References |
| 234 | /// - [POSIX] |
| 235 | /// - [Linux `fallocate`] |
| 236 | /// - [Linux `posix_fallocate`] |
| 237 | /// |
| 238 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/posix_fallocate.html |
| 239 | /// [Linux `fallocate`]: https://man7.org/linux/man-pages/man2/fallocate.2.html |
| 240 | /// [Linux `posix_fallocate`]: https://man7.org/linux/man-pages/man3/posix_fallocate.3.html |
| 241 | #[cfg (not(any( |
| 242 | netbsdlike, |
| 243 | target_os = "dragonfly" , |
| 244 | target_os = "espidf" , |
| 245 | target_os = "nto" , |
| 246 | target_os = "redox" , |
| 247 | target_os = "vita" , |
| 248 | )))] // not implemented in libc for netbsd yet |
| 249 | #[inline ] |
| 250 | #[doc (alias = "posix_fallocate" )] |
| 251 | pub fn fallocate<Fd: AsFd>(fd: Fd, mode: FallocateFlags, offset: u64, len: u64) -> io::Result<()> { |
| 252 | backend::fs::syscalls::fallocate(fd.as_fd(), mode, offset, len) |
| 253 | } |
| 254 | |
| 255 | /// `fcntl(fd, F_GETFL) & O_ACCMODE` |
| 256 | /// |
| 257 | /// Returns a pair of booleans indicating whether the file descriptor is |
| 258 | /// readable and/or writable, respectively. This is only reliable on files; for |
| 259 | /// example, it doesn't reflect whether sockets have been shut down; for |
| 260 | /// general I/O handle support, use [`io::is_read_write`]. |
| 261 | #[inline ] |
| 262 | pub fn is_file_read_write<Fd: AsFd>(fd: Fd) -> io::Result<(bool, bool)> { |
| 263 | _is_file_read_write(fd.as_fd()) |
| 264 | } |
| 265 | |
| 266 | pub(crate) fn _is_file_read_write(fd: BorrowedFd<'_>) -> io::Result<(bool, bool)> { |
| 267 | let mode: OFlags = backend::fs::syscalls::fcntl_getfl(fd)?; |
| 268 | |
| 269 | // Check for `O_PATH`. |
| 270 | #[cfg (any(linux_kernel, target_os = "emscripten" , target_os = "fuchsia" ))] |
| 271 | if mode.contains(OFlags::PATH) { |
| 272 | return Ok((false, false)); |
| 273 | } |
| 274 | |
| 275 | // Use `RWMODE` rather than `ACCMODE` as `ACCMODE` may include `O_PATH`. |
| 276 | // We handled `O_PATH` above. |
| 277 | match mode & OFlags::RWMODE { |
| 278 | OFlags::RDONLY => Ok((true, false)), |
| 279 | OFlags::RDWR => Ok((true, true)), |
| 280 | OFlags::WRONLY => Ok((false, true)), |
| 281 | _ => unreachable!(), |
| 282 | } |
| 283 | } |
| 284 | |
| 285 | /// `fsync(fd)`—Ensures that file data and metadata is written to the |
| 286 | /// underlying storage device. |
| 287 | /// |
| 288 | /// On iOS and macOS this isn't sufficient to ensure that data has reached |
| 289 | /// persistent storage; use [`fcntl_fullfsync`] to ensure that. |
| 290 | /// |
| 291 | /// # References |
| 292 | /// - [POSIX] |
| 293 | /// - [Linux] |
| 294 | /// |
| 295 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fsync.html |
| 296 | /// [Linux]: https://man7.org/linux/man-pages/man2/fsync.2.html |
| 297 | /// [`fcntl_fullfsync`]: https://docs.rs/rustix/*/x86_64-apple-darwin/rustix/fs/fn.fcntl_fullfsync.html |
| 298 | #[inline ] |
| 299 | pub fn fsync<Fd: AsFd>(fd: Fd) -> io::Result<()> { |
| 300 | backend::fs::syscalls::fsync(fd.as_fd()) |
| 301 | } |
| 302 | |
| 303 | /// `fdatasync(fd)`—Ensures that file data is written to the underlying |
| 304 | /// storage device. |
| 305 | /// |
| 306 | /// # References |
| 307 | /// - [POSIX] |
| 308 | /// - [Linux] |
| 309 | /// |
| 310 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/fdatasync.html |
| 311 | /// [Linux]: https://man7.org/linux/man-pages/man2/fdatasync.2.html |
| 312 | #[cfg (not(any( |
| 313 | apple, |
| 314 | target_os = "dragonfly" , |
| 315 | target_os = "espidf" , |
| 316 | target_os = "haiku" , |
| 317 | target_os = "redox" , |
| 318 | target_os = "vita" , |
| 319 | )))] |
| 320 | #[inline ] |
| 321 | pub fn fdatasync<Fd: AsFd>(fd: Fd) -> io::Result<()> { |
| 322 | backend::fs::syscalls::fdatasync(fd.as_fd()) |
| 323 | } |
| 324 | |
| 325 | /// `ftruncate(fd, length)`—Sets the length of a file. |
| 326 | /// |
| 327 | /// # References |
| 328 | /// - [POSIX] |
| 329 | /// - [Linux] |
| 330 | /// |
| 331 | /// [POSIX]: https://pubs.opengroup.org/onlinepubs/9799919799/functions/ftruncate.html |
| 332 | /// [Linux]: https://man7.org/linux/man-pages/man2/ftruncate.2.html |
| 333 | #[inline ] |
| 334 | pub fn ftruncate<Fd: AsFd>(fd: Fd, length: u64) -> io::Result<()> { |
| 335 | backend::fs::syscalls::ftruncate(fd.as_fd(), length) |
| 336 | } |
| 337 | |
| 338 | /// `flock(fd, operation)`—Acquire or release an advisory lock on an open file. |
| 339 | /// |
| 340 | /// # References |
| 341 | /// - [Linux] |
| 342 | /// |
| 343 | /// [Linux]: https://man7.org/linux/man-pages/man2/flock.2.html |
| 344 | #[cfg (not(any( |
| 345 | target_os = "espidf" , |
| 346 | target_os = "solaris" , |
| 347 | target_os = "vita" , |
| 348 | target_os = "wasi" |
| 349 | )))] |
| 350 | #[inline ] |
| 351 | pub fn flock<Fd: AsFd>(fd: Fd, operation: FlockOperation) -> io::Result<()> { |
| 352 | backend::fs::syscalls::flock(fd.as_fd(), operation) |
| 353 | } |
| 354 | |
| 355 | /// `syncfs(fd)`—Flush cached filesystem data. |
| 356 | /// |
| 357 | /// # References |
| 358 | /// - [Linux] |
| 359 | /// |
| 360 | /// [Linux]: https://man7.org/linux/man-pages/man2/syncfs.2.html |
| 361 | #[cfg (linux_kernel)] |
| 362 | #[inline ] |
| 363 | pub fn syncfs<Fd: AsFd>(fd: Fd) -> io::Result<()> { |
| 364 | backend::fs::syscalls::syncfs(fd.as_fd()) |
| 365 | } |
| 366 | |