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