Skip to content

Commit f4cc6e5

Browse files
author
Stjepan Glavina
committed
Define new extension traits
1 parent 6cc5c5f commit f4cc6e5

File tree

2 files changed

+264
-18
lines changed

2 files changed

+264
-18
lines changed

Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,9 @@ readme = "README.md"
1515
[dependencies]
1616
blocking = "1.0.0"
1717
futures-lite = "1.2.0"
18+
19+
[target.'cfg(unix)'.dev-dependencies]
20+
libc = "0.2.78"
21+
22+
[target.'cfg(windows)'.dev-dependencies]
23+
winapi = "0.3.9"

src/lib.rs

Lines changed: 258 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,12 @@ use std::pin::Pin;
3434
use std::sync::Arc;
3535
use std::task::{Context, Poll};
3636

37+
#[cfg(unix)]
38+
use std::os::unix::fs::{DirEntryExt as _, OpenOptionsExt as _};
39+
40+
#[cfg(windows)]
41+
use std::os::windows::fs::OpenOptionsExt as _;
42+
3743
use blocking::{unblock, Unblock};
3844
use futures_lite::io::{AsyncRead, AsyncSeek, AsyncWrite, AsyncWriteExt};
3945
use futures_lite::stream::Stream;
@@ -247,8 +253,7 @@ pub async fn read<P: AsRef<Path>>(path: P) -> io::Result<Vec<u8>> {
247253
///
248254
/// let mut entries = async_fs::read_dir(".").await?;
249255
///
250-
/// while let Some(res) = entries.next().await {
251-
/// let entry = res?;
256+
/// while let Some(entry) = entries.try_next().await? {
252257
/// println!("{}", entry.file_name().to_string_lossy());
253258
/// }
254259
/// # std::io::Result::Ok(()) });
@@ -326,8 +331,7 @@ impl DirEntry {
326331
/// # futures_lite::future::block_on(async {
327332
/// let mut dir = async_fs::read_dir(".").await?;
328333
///
329-
/// while let Some(res) = dir.next().await {
330-
/// let entry = res?;
334+
/// while let Some(entry) = dir.try_next().await? {
331335
/// println!("{:?}", entry.path());
332336
/// }
333337
/// # std::io::Result::Ok(()) });
@@ -359,8 +363,7 @@ impl DirEntry {
359363
/// # futures_lite::future::block_on(async {
360364
/// let mut dir = async_fs::read_dir(".").await?;
361365
///
362-
/// while let Some(res) = dir.next().await {
363-
/// let entry = res?;
366+
/// while let Some(entry) = dir.try_next().await? {
364367
/// println!("{:?}", entry.metadata().await?);
365368
/// }
366369
/// # std::io::Result::Ok(()) });
@@ -392,8 +395,7 @@ impl DirEntry {
392395
/// # futures_lite::future::block_on(async {
393396
/// let mut dir = async_fs::read_dir(".").await?;
394397
///
395-
/// while let Some(res) = dir.next().await {
396-
/// let entry = res?;
398+
/// while let Some(entry) = dir.try_next().await? {
397399
/// println!("{:?}", entry.file_type().await?);
398400
/// }
399401
/// # std::io::Result::Ok(()) });
@@ -413,8 +415,7 @@ impl DirEntry {
413415
/// # futures_lite::future::block_on(async {
414416
/// let mut dir = async_fs::read_dir(".").await?;
415417
///
416-
/// while let Some(res) = dir.next().await {
417-
/// let entry = res?;
418+
/// while let Some(entry) = dir.try_next().await? {
418419
/// println!("{}", entry.file_name().to_string_lossy());
419420
/// }
420421
/// # std::io::Result::Ok(()) });
@@ -437,7 +438,7 @@ impl Clone for DirEntry {
437438
}
438439

439440
#[cfg(unix)]
440-
impl std::os::unix::fs::DirEntryExt for DirEntry {
441+
impl unix::DirEntryExt for DirEntry {
441442
fn ino(&self) -> u64 {
442443
self.0.ino()
443444
}
@@ -764,7 +765,7 @@ impl DirBuilder {
764765
}
765766

766767
#[cfg(unix)]
767-
impl std::os::unix::fs::DirBuilderExt for DirBuilder {
768+
impl unix::DirBuilderExt for DirBuilder {
768769
fn mode(&mut self, mode: u32) -> &mut Self {
769770
self.mode = Some(mode);
770771
self
@@ -1428,7 +1429,7 @@ impl Default for OpenOptions {
14281429
}
14291430

14301431
#[cfg(unix)]
1431-
impl std::os::unix::fs::OpenOptionsExt for OpenOptions {
1432+
impl unix::OpenOptionsExt for OpenOptions {
14321433
fn mode(&mut self, mode: u32) -> &mut Self {
14331434
self.0.mode(mode);
14341435
self
@@ -1441,7 +1442,7 @@ impl std::os::unix::fs::OpenOptionsExt for OpenOptions {
14411442
}
14421443

14431444
#[cfg(windows)]
1444-
impl std::os::windows::fs::OpenOptionsExt for OpenOptions {
1445+
impl windows::OpenOptionsExt for OpenOptions {
14451446
fn access_mode(&mut self, access: u32) -> &mut Self {
14461447
self.0.access_mode(access);
14471448
self
@@ -1474,9 +1475,7 @@ pub mod unix {
14741475
use super::*;
14751476

14761477
#[doc(no_inline)]
1477-
pub use std::os::unix::fs::{
1478-
DirBuilderExt, DirEntryExt, FileTypeExt, MetadataExt, OpenOptionsExt, PermissionsExt,
1479-
};
1478+
pub use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt};
14801479

14811480
/// Creates a new symbolic link on the filesystem.
14821481
///
@@ -1494,6 +1493,92 @@ pub mod unix {
14941493
let dst = dst.as_ref().to_owned();
14951494
unblock(move || std::os::unix::fs::symlink(&src, &dst)).await
14961495
}
1496+
1497+
/// Unix-specific extensions to [`DirBuilder`].
1498+
pub trait DirBuilderExt {
1499+
/// Sets the mode to create new directories with.
1500+
///
1501+
/// This option defaults to `0o777`.
1502+
///
1503+
/// # Examples
1504+
///
1505+
/// ```no_run
1506+
/// use async_fs::{DirBuilder, unix::DirBuilderExt};
1507+
///
1508+
/// let mut builder = DirBuilder::new();
1509+
/// builder.mode(0o755);
1510+
/// ```
1511+
fn mode(&mut self, mode: u32) -> &mut Self;
1512+
}
1513+
1514+
/// Unix-specific extension methods for [`DirEntry`].
1515+
pub trait DirEntryExt {
1516+
/// Returns the underlying `d_ino` field in the contained `dirent` structure.
1517+
///
1518+
/// # Examples
1519+
///
1520+
/// ```no_run
1521+
/// use async_fs::unix::DirEntryExt;
1522+
/// use futures_lite::stream::StreamExt;
1523+
///
1524+
/// # futures_lite::future::block_on(async {
1525+
/// let mut entries = async_fs::read_dir(".").await?;
1526+
///
1527+
/// while let Some(entry) = entries.try_next().await? {
1528+
/// println!("{:?}: {}", entry.file_name(), entry.ino());
1529+
/// }
1530+
/// # std::io::Result::Ok(()) });
1531+
/// ```
1532+
fn ino(&self) -> u64;
1533+
}
1534+
1535+
/// Unix-specific extensions to [`OpenOptions`].
1536+
pub trait OpenOptionsExt {
1537+
/// Sets the mode bits that a new file will be created with.
1538+
///
1539+
/// If a new file is created as part of an [`OpenOptions::open()`] call then this
1540+
/// specified `mode` will be used as the permission bits for the new file.
1541+
///
1542+
/// If no `mode` is set, the default of `0o666` will be used.
1543+
/// The operating system masks out bits with the system's `umask`, to produce
1544+
/// the final permissions.
1545+
///
1546+
/// # Examples
1547+
///
1548+
/// ```no_run
1549+
/// use async_fs::{OpenOptions, unix::OpenOptionsExt};
1550+
///
1551+
/// # futures_lite::future::block_on(async {
1552+
/// let mut options = OpenOptions::new();
1553+
/// // Read/write permissions for owner and read permissions for others.
1554+
/// options.mode(0o644);
1555+
/// let file = options.open("foo.txt").await?;
1556+
/// # std::io::Result::Ok(()) });
1557+
/// ```
1558+
fn mode(&mut self, mode: u32) -> &mut Self;
1559+
1560+
/// Passes custom flags to the `flags` argument of `open`.
1561+
///
1562+
/// The bits that define the access mode are masked out with `O_ACCMODE`, to
1563+
/// ensure they do not interfere with the access mode set by Rust's options.
1564+
///
1565+
/// Custom flags can only set flags, not remove flags set by Rust's options.
1566+
/// This options overwrites any previously set custom flags.
1567+
///
1568+
/// # Examples
1569+
///
1570+
/// ```no_run
1571+
/// use async_fs::{OpenOptions, unix::OpenOptionsExt};
1572+
///
1573+
/// # futures_lite::future::block_on(async {
1574+
/// let mut options = OpenOptions::new();
1575+
/// options.write(true);
1576+
/// options.custom_flags(libc::O_NOFOLLOW);
1577+
/// let file = options.open("foo.txt").await?;
1578+
/// # std::io::Result::Ok(()) });
1579+
/// ```
1580+
fn custom_flags(&mut self, flags: i32) -> &mut Self;
1581+
}
14971582
}
14981583

14991584
/// Windows-specific extensions.
@@ -1502,7 +1587,7 @@ pub mod windows {
15021587
use super::*;
15031588

15041589
#[doc(no_inline)]
1505-
pub use std::os::windows::fs::{MetadataExt, OpenOptionsExt};
1590+
pub use std::os::windows::fs::MetadataExt;
15061591

15071592
/// Creates a new directory symbolic link on the filesystem.
15081593
///
@@ -1537,4 +1622,159 @@ pub mod windows {
15371622
let dst = dst.as_ref().to_owned();
15381623
unblock(move || std::os::windows::fs::symlink_file(&src, &dst)).await
15391624
}
1625+
1626+
/// Windows-specific extensions to [`OpenOptions`].
1627+
pub trait OpenOptionsExt {
1628+
/// Overrides the `dwDesiredAccess` argument to the call to [`CreateFile`]
1629+
/// with the specified value.
1630+
///
1631+
/// This will override the `read`, `write`, and `append` flags on the
1632+
/// [`OpenOptions`] structure. This method provides fine-grained control over
1633+
/// the permissions to read, write and append data, attributes (like hidden
1634+
/// and system), and extended attributes.
1635+
///
1636+
/// # Examples
1637+
///
1638+
/// ```no_run
1639+
/// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1640+
///
1641+
/// # futures_lite::future::block_on(async {
1642+
/// // Open without read and write permission, for example if you only need
1643+
/// // to call `stat` on the file
1644+
/// let file = OpenOptions::new().access_mode(0).open("foo.txt").await?;
1645+
/// # std::io::Result::Ok(()) });
1646+
/// ```
1647+
///
1648+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1649+
fn access_mode(&mut self, access: u32) -> &mut Self;
1650+
1651+
/// Overrides the `dwShareMode` argument to the call to [`CreateFile`] with
1652+
/// the specified value.
1653+
///
1654+
/// By default `share_mode` is set to
1655+
/// `FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE`. This allows
1656+
/// other processes to read, write, and delete/rename the same file
1657+
/// while it is open. Removing any of the flags will prevent other
1658+
/// processes from performing the corresponding operation until the file
1659+
/// handle is closed.
1660+
///
1661+
/// # Examples
1662+
///
1663+
/// ```no_run
1664+
/// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1665+
///
1666+
/// # futures_lite::future::block_on(async {
1667+
/// // Do not allow others to read or modify this file while we have it open
1668+
/// // for writing.
1669+
/// let file = OpenOptions::new()
1670+
/// .write(true)
1671+
/// .share_mode(0)
1672+
/// .open("foo.txt")
1673+
/// .await?;
1674+
/// # std::io::Result::Ok(()) });
1675+
/// ```
1676+
///
1677+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1678+
fn share_mode(&mut self, val: u32) -> &mut Self;
1679+
1680+
/// Sets extra flags for the `dwFileFlags` argument to the call to
1681+
/// [`CreateFile2`] to the specified value (or combines it with
1682+
/// `attributes` and `security_qos_flags` to set the `dwFlagsAndAttributes`
1683+
/// for [`CreateFile`]).
1684+
///
1685+
/// Custom flags can only set flags, not remove flags set by Rust's options.
1686+
/// This option overwrites any previously set custom flags.
1687+
///
1688+
/// # Examples
1689+
///
1690+
/// ```no_run
1691+
/// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1692+
///
1693+
/// # futures_lite::future::block_on(async {
1694+
/// let file = OpenOptions::new()
1695+
/// .create(true)
1696+
/// .write(true)
1697+
/// .custom_flags(winapi::um::winbase::FILE_FLAG_DELETE_ON_CLOSE)
1698+
/// .open("foo.txt")
1699+
/// .await?;
1700+
/// # std::io::Result::Ok(()) });
1701+
/// ```
1702+
///
1703+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1704+
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
1705+
fn custom_flags(&mut self, flags: u32) -> &mut Self;
1706+
1707+
/// Sets the `dwFileAttributes` argument to the call to [`CreateFile2`] to
1708+
/// the specified value (or combines it with `custom_flags` and
1709+
/// `security_qos_flags` to set the `dwFlagsAndAttributes` for
1710+
/// [`CreateFile`]).
1711+
///
1712+
/// If a _new_ file is created because it does not yet exist and
1713+
/// `.create(true)` or `.create_new(true)` are specified, the new file is
1714+
/// given the attributes declared with `.attributes()`.
1715+
///
1716+
/// If an _existing_ file is opened with `.create(true).truncate(true)`, its
1717+
/// existing attributes are preserved and combined with the ones declared
1718+
/// with `.attributes()`.
1719+
///
1720+
/// In all other cases the attributes get ignored.
1721+
///
1722+
/// # Examples
1723+
///
1724+
/// ```no_run
1725+
/// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1726+
///
1727+
/// # futures_lite::future::block_on(async {
1728+
/// let file = OpenOptions::new()
1729+
/// .write(true)
1730+
/// .create(true)
1731+
/// .attributes(winapi::um::winnt::FILE_ATTRIBUTE_HIDDEN)
1732+
/// .open("foo.txt")
1733+
/// .await?;
1734+
/// # std::io::Result::Ok(()) });
1735+
/// ```
1736+
///
1737+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1738+
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
1739+
fn attributes(&mut self, val: u32) -> &mut Self;
1740+
1741+
/// Sets the `dwSecurityQosFlags` argument to the call to [`CreateFile2`] to
1742+
/// the specified value (or combines it with `custom_flags` and `attributes`
1743+
/// to set the `dwFlagsAndAttributes` for [`CreateFile`]).
1744+
///
1745+
/// By default `security_qos_flags` is not set. It should be specified when
1746+
/// opening a named pipe, to control to which degree a server process can
1747+
/// act on behalf of a client process (security impersonation level).
1748+
///
1749+
/// When `security_qos_flags` is not set, a malicious program can gain the
1750+
/// elevated privileges of a privileged Rust process when it allows opening
1751+
/// user-specified paths, by tricking it into opening a named pipe. So
1752+
/// arguably `security_qos_flags` should also be set when opening arbitrary
1753+
/// paths. However the bits can then conflict with other flags, specifically
1754+
/// `FILE_FLAG_OPEN_NO_RECALL`.
1755+
///
1756+
/// For information about possible values, see [Impersonation Levels] on the
1757+
/// Windows Dev Center site. The `SECURITY_SQOS_PRESENT` flag is set
1758+
/// automatically when using this method.
1759+
///
1760+
/// # Examples
1761+
///
1762+
/// ```no_run
1763+
/// use async_fs::{OpenOptions, windows::OpenOptionsExt};
1764+
///
1765+
/// # futures_lite::future::block_on(async {
1766+
/// let file = OpenOptions::new()
1767+
/// .write(true)
1768+
/// .create(true)
1769+
/// .security_qos_flags(winapi::um::winbase::SECURITY_IDENTIFICATION)
1770+
/// .open(r"\\.\pipe\MyPipe")
1771+
/// .await?;
1772+
/// # std::io::Result::Ok(()) });
1773+
/// ```
1774+
///
1775+
/// [`CreateFile`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilea
1776+
/// [`CreateFile2`]: https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfile2
1777+
/// [Impersonation Levels]: https://docs.microsoft.com/en-us/windows/win32/api/winnt/ne-winnt-security_impersonation_level
1778+
fn security_qos_flags(&mut self, flags: u32) -> &mut Self;
1779+
}
15401780
}

0 commit comments

Comments
 (0)