1 //! POSIX-style filesystem functions which operate on bare paths.
2 
3 use crate::fd::OwnedFd;
4 #[cfg(not(any(target_os = "espidf", target_os = "vita")))]
5 use 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 )))]
16 use crate::fs::StatFs;
17 #[cfg(not(any(target_os = "haiku", target_os = "redox", target_os = "wasi")))]
18 use crate::fs::StatVfs;
19 use crate::fs::{Mode, OFlags, Stat};
20 #[cfg(not(target_os = "wasi"))]
21 use crate::ugid::{Gid, Uid};
22 use crate::{backend, io, path};
23 #[cfg(feature = "alloc")]
24 use {
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]
open<P: path::Arg>(path: P, flags: OFlags, mode: Mode) -> io::Result<OwnedFd>45 pub fn open<P: path::Arg>(path: P, flags: OFlags, mode: Mode) -> io::Result<OwnedFd> {
46     path.into_with_c_str(|path| 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]
chmod<P: path::Arg>(path: P, mode: Mode) -> io::Result<()>59 pub fn chmod<P: path::Arg>(path: P, mode: Mode) -> io::Result<()> {
60     path.into_with_c_str(|path| 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]
stat<P: path::Arg>(path: P) -> io::Result<Stat>77 pub 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]
lstat<P: path::Arg>(path: P) -> io::Result<Stat>96 pub 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]
readlink<P: path::Arg, B: Into<Vec<u8>>>(path: P, reuse: B) -> io::Result<CString>112 pub fn readlink<P: path::Arg, B: Into<Vec<u8>>>(path: P, reuse: B) -> io::Result<CString> {
113     path.into_with_c_str(|path| _readlink(path, reuse.into()))
114 }
115 
116 #[cfg(feature = "alloc")]
_readlink(path: &CStr, mut buffer: Vec<u8>) -> io::Result<CString>117 fn _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(buffer.capacity(), 0_u8);
123 
124     loop {
125         let nread = backend::fs::syscalls::readlink(path, &mut buffer)?;
126 
127         let nread = nread as usize;
128         assert!(nread <= buffer.len());
129         if nread < buffer.len() {
130             buffer.resize(nread, 0_u8);
131             return Ok(CString::new(buffer).unwrap());
132         }
133         // Use `Vec` reallocation strategy to grow capacity exponentially.
134         buffer.reserve(1);
135         buffer.resize(buffer.capacity(), 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]
rename<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()>148 pub 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| {
150         new_path.into_with_c_str(|new_path| 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]
unlink<P: path::Arg>(path: P) -> io::Result<()>163 pub 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]
rmdir<P: path::Arg>(path: P) -> io::Result<()>176 pub 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]
link<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()>198 pub 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| {
200         new_path.into_with_c_str(|new_path| 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]
symlink<P: path::Arg, Q: path::Arg>(old_path: P, new_path: Q) -> io::Result<()>213 pub 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| {
215         new_path.into_with_c_str(|new_path| 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]
mkdir<P: path::Arg>(path: P, mode: Mode) -> io::Result<()>228 pub fn mkdir<P: path::Arg>(path: P, mode: Mode) -> io::Result<()> {
229     path.into_with_c_str(|path| 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]
access<P: path::Arg>(path: P, access: Access) -> io::Result<()>242 pub fn access<P: path::Arg>(path: P, access: Access) -> io::Result<()> {
243     path.into_with_c_str(|path| 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]
statfs<P: path::Arg>(path: P) -> io::Result<StatFs>266 pub 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]
statvfs<P: path::Arg>(path: P) -> io::Result<StatVfs>285 pub 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]
chown<P: path::Arg>(path: P, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()>299 pub fn chown<P: path::Arg>(path: P, owner: Option<Uid>, group: Option<Gid>) -> io::Result<()> {
300     path.into_with_c_str(|path| backend::fs::syscalls::chown(path, owner, group))
301 }
302