| 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/9799919799/functions/open.html |
| 43 | /// [Linux]: https://man7.org/linux/man-pages/man2/open.2.html |
| 44 | #[inline ] |
| 45 | pub 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/9799919799/functions/chmod.html |
| 56 | /// [Linux]: https://man7.org/linux/man-pages/man2/chmod.2.html |
| 57 | #[cfg (not(target_os = "wasi" ))] |
| 58 | #[inline ] |
| 59 | pub 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/9799919799/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 ] |
| 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/9799919799/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 ] |
| 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/9799919799/functions/readlink.html |
| 109 | /// [Linux]: https://man7.org/linux/man-pages/man2/readlink.2.html |
| 110 | #[cfg (feature = "alloc" )] |
| 111 | #[inline ] |
| 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: &CStr| _readlink(path, buffer:reuse.into())) |
| 114 | } |
| 115 | |
| 116 | #[cfg (feature = "alloc" )] |
| 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(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/9799919799/functions/rename.html |
| 146 | /// [Linux]: https://man7.org/linux/man-pages/man2/rename.2.html |
| 147 | #[inline ] |
| 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: &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/9799919799/functions/unlink.html |
| 161 | /// [Linux]: https://man7.org/linux/man-pages/man2/unlink.2.html |
| 162 | #[inline ] |
| 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/9799919799/functions/rmdir.html |
| 174 | /// [Linux]: https://man7.org/linux/man-pages/man2/rmdir.2.html |
| 175 | #[inline ] |
| 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/9799919799/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 ] |
| 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: &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/9799919799/functions/symlink.html |
| 211 | /// [Linux]: https://man7.org/linux/man-pages/man2/symlink.2.html |
| 212 | #[inline ] |
| 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: &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/9799919799/functions/mkdir.html |
| 226 | /// [Linux]: https://man7.org/linux/man-pages/man2/mkdir.2.html |
| 227 | #[inline ] |
| 228 | pub 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/9799919799/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 ] |
| 242 | pub 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 ] |
| 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/9799919799/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 ] |
| 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/9799919799/functions/chown.html |
| 296 | /// [Linux]: https://man7.org/linux/man-pages/man2/chown.2.html |
| 297 | #[cfg (not(target_os = "wasi" ))] |
| 298 | #[inline ] |
| 299 | pub 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 | |