1 | //! POSIX-style filesystem functions which operate on bare paths. |
2 | |
3 | use crate::fd::OwnedFd; |
4 | #[cfg (not(target_os = "espidf" ))] |
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 = "wasi" , |
14 | )))] |
15 | use crate::fs::StatFs; |
16 | #[cfg (not(any(target_os = "haiku" , target_os = "redox" , target_os = "wasi" )))] |
17 | use crate::fs::StatVfs; |
18 | use crate::fs::{Mode, OFlags, Stat}; |
19 | #[cfg (not(target_os = "wasi" ))] |
20 | use crate::ugid::{Gid, Uid}; |
21 | use crate::{backend, io, path}; |
22 | #[cfg (feature = "alloc" )] |
23 | use { |
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 ] |
44 | pub 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 ] |
58 | pub 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 ] |
76 | pub 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 ] |
95 | pub 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 ] |
111 | pub 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" )] |
116 | fn _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 ] |
147 | pub 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 ] |
162 | pub 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 ] |
175 | pub 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 ] |
197 | pub 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 ] |
212 | pub 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 ] |
227 | pub 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 ] |
241 | pub 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 ] |
264 | pub 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 ] |
283 | pub 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 ] |
297 | pub 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 | |