1//! POSIX-style filesystem functions which operate on bare paths.
2
3use crate::fd::OwnedFd;
4#[cfg(not(target_os = "espidf"))]
5use crate::fs::Access;
6#[cfg(not(any(
7 solarish,
8 target_os = "espidf",
9 target_os = "haiku",
10 target_os = "netbsd",
11 target_os = "nto",
12 target_os = "redox",
13 target_os = "wasi",
14)))]
15use crate::fs::StatFs;
16#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
17use crate::fs::StatVfs;
18use crate::fs::{Mode, OFlags, Stat};
19#[cfg(not(target_os = "wasi"))]
20use crate::ugid::{Gid, Uid};
21use crate::{backend, io, path};
22#[cfg(feature = "alloc")]
23use {
24 crate::ffi::{CStr, CString},
25 crate::path::SMALL_PATH_BUFFER_SIZE,
26 alloc::vec::Vec,
27};
28
29/// `open(path, oflags, mode)`—Opens a file.
30///
31/// POSIX guarantees that `open` will use the lowest unused file descriptor,
32/// however it is not safe in general to rely on this, as file descriptors may
33/// be unexpectedly allocated on other threads or in libraries.
34///
35/// The `Mode` argument is only significant when creating a file.
36///
37/// # References
38/// - [POSIX]
39/// - [Linux]
40///
41/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html
42/// [Linux]: https://man7.org/linux/man-pages/man2/open.2.html
43#[inline]
44pub fn open<P: path::Arg>(path: P, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> {
45 path.into_with_c_str(|path: &CStr| backend::fs::syscalls::open(path, flags, mode))
46}
47
48/// `chmod(path, mode)`—Sets file or directory permissions.
49///
50/// # References
51/// - [POSIX]
52/// - [Linux]
53///
54/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/chmod.html
55/// [Linux]: https://man7.org/linux/man-pages/man2/chmod.2.html
56#[cfg(not(target_os = "wasi"))]
57#[inline]
58pub fn chmod<P: path::Arg>(path: P, mode: Mode) -> io::Result<()> {
59 path.into_with_c_str(|path: &CStr| backend::fs::syscalls::chmod(path, mode))
60}
61
62/// `stat(path)`—Queries metadata for a file or directory.
63///
64/// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to
65/// interpret the `st_mode` field.
66///
67/// # References
68/// - [POSIX]
69/// - [Linux]
70///
71/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/stat.html
72/// [Linux]: https://man7.org/linux/man-pages/man2/stat.2.html
73/// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode
74/// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode
75#[inline]
76pub fn stat<P: path::Arg>(path: P) -> io::Result<Stat> {
77 path.into_with_c_str(backend::fs::syscalls::stat)
78}
79
80/// `lstat(path)`—Queries metadata for a file or directory, without following
81/// symlinks.
82///
83/// [`Mode::from_raw_mode`] and [`FileType::from_raw_mode`] may be used to
84/// interpret the `st_mode` field.
85///
86/// # References
87/// - [POSIX]
88/// - [Linux]
89///
90/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/lstat.html
91/// [Linux]: https://man7.org/linux/man-pages/man2/lstat.2.html
92/// [`Mode::from_raw_mode`]: crate::fs::Mode::from_raw_mode
93/// [`FileType::from_raw_mode`]: crate::fs::FileType::from_raw_mode
94#[inline]
95pub fn lstat<P: path::Arg>(path: P) -> io::Result<Stat> {
96 path.into_with_c_str(backend::fs::syscalls::lstat)
97}
98
99/// `readlink(path)`—Reads the contents of a symlink.
100///
101/// If `reuse` is non-empty, reuse its buffer to store the result if possible.
102///
103/// # References
104/// - [POSIX]
105/// - [Linux]
106///
107/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/readlink.html
108/// [Linux]: https://man7.org/linux/man-pages/man2/readlink.2.html
109#[cfg(feature = "alloc")]
110#[inline]
111pub fn readlink<P: path::Arg, B: Into<Vec<u8>>>(path: P, reuse: B) -> io::Result<CString> {
112 path.into_with_c_str(|path: &CStr| _readlink(path, buffer:reuse.into()))
113}
114
115#[cfg(feature = "alloc")]
116fn _readlink(path: &CStr, mut buffer: Vec<u8>) -> io::Result<CString> {
117 // This code would benefit from having a better way to read into
118 // uninitialized memory, but that requires `unsafe`.
119 buffer.clear();
120 buffer.reserve(SMALL_PATH_BUFFER_SIZE);
121 buffer.resize(new_len:buffer.capacity(), value:0_u8);
122
123 loop {
124 let nread: usize = backend::fs::syscalls::readlink(path, &mut buffer)?;
125
126 let nread: usize = nread as usize;
127 assert!(nread <= buffer.len());
128 if nread < buffer.len() {
129 buffer.resize(new_len:nread, value:0_u8);
130 return Ok(CString::new(buffer).unwrap());
131 }
132 // Use `Vec` reallocation strategy to grow capacity exponentially.
133 buffer.reserve(additional:1);
134 buffer.resize(new_len:buffer.capacity(), value:0_u8);
135 }
136}
137
138/// `rename(old_path, new_path)`—Renames a file or directory.
139///
140/// # References
141/// - [POSIX]
142/// - [Linux]
143///
144/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/rename.html
145/// [Linux]: https://man7.org/linux/man-pages/man2/rename.2.html
146#[inline]
147pub fn rename<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()> {
148 old_path.into_with_c_str(|old_path: &CStr| {
149 new_path.into_with_c_str(|new_path: &CStr| backend::fs::syscalls::rename(old_path, new_path))
150 })
151}
152
153/// `unlink(path)`—Unlinks a file.
154///
155/// # References
156/// - [POSIX]
157/// - [Linux]
158///
159/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html
160/// [Linux]: https://man7.org/linux/man-pages/man2/unlink.2.html
161#[inline]
162pub fn unlink<P: path::Arg>(path: P) -> io::Result<()> {
163 path.into_with_c_str(backend::fs::syscalls::unlink)
164}
165
166/// `rmdir(path)`—Removes a directory.
167///
168/// # References
169/// - [POSIX]
170/// - [Linux]
171///
172/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/rmdir.html
173/// [Linux]: https://man7.org/linux/man-pages/man2/rmdir.2.html
174#[inline]
175pub fn rmdir<P: path::Arg>(path: P) -> io::Result<()> {
176 path.into_with_c_str(backend::fs::syscalls::rmdir)
177}
178
179/// `link(old_path, new_path)`—Creates a hard link.
180///
181/// POSIX leaves it implementation-defined whether `link` follows a symlink in
182/// `old_path`, or creates a new link to the symbolic link itself. On platforms
183/// which have it, [`linkat`] avoids this problem since it has an [`AtFlags`]
184/// paramter and the [`AtFlags::SYMLINK_FOLLOW`] flag determines whether
185/// symlinks should be followed.
186///
187/// # References
188/// - [POSIX]
189/// - [Linux]
190///
191/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/link.html
192/// [Linux]: https://man7.org/linux/man-pages/man2/link.2.html
193/// [`linkat`]: crate::fs::linkat
194/// [`AtFlags`]: crate::fs::AtFlags
195/// [`AtFlags::SYMLINK_FOLLOW`]: crate::fs::AtFlags::SYMLINK_FOLLOW
196#[inline]
197pub fn link<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()> {
198 old_path.into_with_c_str(|old_path: &CStr| {
199 new_path.into_with_c_str(|new_path: &CStr| backend::fs::syscalls::link(old_path, new_path))
200 })
201}
202
203/// `symlink(old_path, new_path)`—Creates a symlink.
204///
205/// # References
206/// - [POSIX]
207/// - [Linux]
208///
209/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlink.html
210/// [Linux]: https://man7.org/linux/man-pages/man2/symlink.2.html
211#[inline]
212pub fn symlink<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()> {
213 old_path.into_with_c_str(|old_path: &CStr| {
214 new_path.into_with_c_str(|new_path: &CStr| backend::fs::syscalls::symlink(old_path, new_path))
215 })
216}
217
218/// `mkdir(path, mode)`—Creates a directory.
219///
220/// # References
221/// - [POSIX]
222/// - [Linux]
223///
224/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html
225/// [Linux]: https://man7.org/linux/man-pages/man2/mkdir.2.html
226#[inline]
227pub fn mkdir<P: path::Arg>(path: P, mode: Mode) -> io::Result<()> {
228 path.into_with_c_str(|path: &CStr| backend::fs::syscalls::mkdir(path, mode))
229}
230
231/// `access(path, access)`—Tests permissions for a file or directory.
232///
233/// # References
234/// - [POSIX]
235/// - [Linux]
236///
237/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html
238/// [Linux]: https://man7.org/linux/man-pages/man2/access.2.html
239#[cfg(not(target_os = "espidf"))]
240#[inline]
241pub fn access<P: path::Arg>(path: P, access: Access) -> io::Result<()> {
242 path.into_with_c_str(|path: &CStr| backend::fs::syscalls::access(path, access))
243}
244
245/// `statfs`—Queries filesystem metadata.
246///
247/// Compared to [`statvfs`], this function often provides more information,
248/// though it's less portable.
249///
250/// # References
251/// - [Linux]
252///
253/// [Linux]: https://man7.org/linux/man-pages/man2/statfs.2.html
254#[cfg(not(any(
255 solarish,
256 target_os = "espidf",
257 target_os = "haiku",
258 target_os = "netbsd",
259 target_os = "nto",
260 target_os = "redox",
261 target_os = "wasi",
262)))]
263#[inline]
264pub fn statfs<P: path::Arg>(path: P) -> io::Result<StatFs> {
265 path.into_with_c_str(backend::fs::syscalls::statfs)
266}
267
268/// `statvfs`—Queries filesystem metadata, POSIX version.
269///
270/// Compared to [`statfs`], this function often provides less information,
271/// but it is more portable. But even so, filesystems are very diverse and not
272/// all the fields are meaningful for every filesystem. And `f_fsid` doesn't
273/// seem to have a clear meaning anywhere.
274///
275/// # References
276/// - [POSIX]
277/// - [Linux]
278///
279/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/statvfs.html
280/// [Linux]: https://man7.org/linux/man-pages/man2/statvfs.2.html
281#[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
282#[inline]
283pub fn statvfs<P: path::Arg>(path: P) -> io::Result<StatVfs> {
284 path.into_with_c_str(backend::fs::syscalls::statvfs)
285}
286
287/// `chown(path, owner, group)`—Sets open file or directory ownership.
288///
289/// # References
290/// - [POSIX]
291/// - [Linux]
292///
293/// [POSIX]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html
294/// [Linux]: https://man7.org/linux/man-pages/man2/chown.2.html
295#[cfg(not(target_os = "wasi"))]
296#[inline]
297pub fn chown<P: path::Arg>(path: P, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
298 path.into_with_c_str(|path: &CStr| backend::fs::syscalls::chown(path, owner, group))
299}
300