| 1 | #[cfg (any(apple_targets, target_os = "openbsd" ))] |
| 2 | pub use libc::c_uint; |
| 3 | #[cfg (any(target_os = "netbsd" , freebsdlike))] |
| 4 | pub use libc::c_ulong; |
| 5 | pub use libc::stat as FileStat; |
| 6 | pub use libc::{dev_t, mode_t}; |
| 7 | |
| 8 | #[cfg (not(target_os = "redox" ))] |
| 9 | use crate::fcntl::{at_rawfd, AtFlags}; |
| 10 | use crate::sys::time::{TimeSpec, TimeVal}; |
| 11 | use crate::{errno::Errno, NixPath, Result}; |
| 12 | use std::mem; |
| 13 | use std::os::unix::io::RawFd; |
| 14 | |
| 15 | libc_bitflags!( |
| 16 | /// "File type" flags for `mknod` and related functions. |
| 17 | pub struct SFlag: mode_t { |
| 18 | S_IFIFO; |
| 19 | S_IFCHR; |
| 20 | S_IFDIR; |
| 21 | S_IFBLK; |
| 22 | S_IFREG; |
| 23 | S_IFLNK; |
| 24 | S_IFSOCK; |
| 25 | S_IFMT; |
| 26 | } |
| 27 | ); |
| 28 | |
| 29 | libc_bitflags! { |
| 30 | /// "File mode / permissions" flags. |
| 31 | pub struct Mode: mode_t { |
| 32 | /// Read, write and execute for owner. |
| 33 | S_IRWXU; |
| 34 | /// Read for owner. |
| 35 | S_IRUSR; |
| 36 | /// Write for owner. |
| 37 | S_IWUSR; |
| 38 | /// Execute for owner. |
| 39 | S_IXUSR; |
| 40 | /// Read write and execute for group. |
| 41 | S_IRWXG; |
| 42 | /// Read for group. |
| 43 | S_IRGRP; |
| 44 | /// Write for group. |
| 45 | S_IWGRP; |
| 46 | /// Execute for group. |
| 47 | S_IXGRP; |
| 48 | /// Read, write and execute for other. |
| 49 | S_IRWXO; |
| 50 | /// Read for other. |
| 51 | S_IROTH; |
| 52 | /// Write for other. |
| 53 | S_IWOTH; |
| 54 | /// Execute for other. |
| 55 | S_IXOTH; |
| 56 | /// Set user id on execution. |
| 57 | S_ISUID as mode_t; |
| 58 | /// Set group id on execution. |
| 59 | S_ISGID as mode_t; |
| 60 | S_ISVTX as mode_t; |
| 61 | } |
| 62 | } |
| 63 | |
| 64 | #[cfg (any(apple_targets, target_os = "openbsd" ))] |
| 65 | pub type type_of_file_flag = c_uint; |
| 66 | #[cfg (any(freebsdlike, target_os = "netbsd" ))] |
| 67 | pub type type_of_file_flag = c_ulong; |
| 68 | |
| 69 | #[cfg (bsd)] |
| 70 | libc_bitflags! { |
| 71 | /// File flags. |
| 72 | pub struct FileFlag: type_of_file_flag { |
| 73 | /// The file may only be appended to. |
| 74 | SF_APPEND; |
| 75 | /// The file has been archived. |
| 76 | SF_ARCHIVED; |
| 77 | #[cfg(any(target_os = "dragonfly" ))] |
| 78 | SF_CACHE; |
| 79 | /// The file may not be changed. |
| 80 | SF_IMMUTABLE; |
| 81 | /// Indicates a WAPBL journal file. |
| 82 | #[cfg(any(target_os = "netbsd" ))] |
| 83 | SF_LOG; |
| 84 | /// Do not retain history for file |
| 85 | #[cfg(any(target_os = "dragonfly" ))] |
| 86 | SF_NOHISTORY; |
| 87 | /// The file may not be renamed or deleted. |
| 88 | #[cfg(freebsdlike)] |
| 89 | SF_NOUNLINK; |
| 90 | /// Mask of superuser changeable flags |
| 91 | SF_SETTABLE; |
| 92 | /// Snapshot is invalid. |
| 93 | #[cfg(any(target_os = "netbsd" ))] |
| 94 | SF_SNAPINVAL; |
| 95 | /// The file is a snapshot file. |
| 96 | #[cfg(any(target_os = "netbsd" , target_os = "freebsd" ))] |
| 97 | SF_SNAPSHOT; |
| 98 | #[cfg(any(target_os = "dragonfly" ))] |
| 99 | SF_XLINK; |
| 100 | /// The file may only be appended to. |
| 101 | UF_APPEND; |
| 102 | /// The file needs to be archived. |
| 103 | #[cfg(any(target_os = "freebsd" ))] |
| 104 | UF_ARCHIVE; |
| 105 | #[cfg(any(target_os = "dragonfly" ))] |
| 106 | UF_CACHE; |
| 107 | /// File is compressed at the file system level. |
| 108 | #[cfg(apple_targets)] |
| 109 | UF_COMPRESSED; |
| 110 | /// The file may be hidden from directory listings at the application's |
| 111 | /// discretion. |
| 112 | #[cfg(any( |
| 113 | target_os = "freebsd" , |
| 114 | apple_targets, |
| 115 | ))] |
| 116 | UF_HIDDEN; |
| 117 | /// The file may not be changed. |
| 118 | UF_IMMUTABLE; |
| 119 | /// Do not dump the file. |
| 120 | UF_NODUMP; |
| 121 | #[cfg(any(target_os = "dragonfly" ))] |
| 122 | UF_NOHISTORY; |
| 123 | /// The file may not be renamed or deleted. |
| 124 | #[cfg(freebsdlike)] |
| 125 | UF_NOUNLINK; |
| 126 | /// The file is offline, or has the Windows and CIFS |
| 127 | /// `FILE_ATTRIBUTE_OFFLINE` attribute. |
| 128 | #[cfg(any(target_os = "freebsd" ))] |
| 129 | UF_OFFLINE; |
| 130 | /// The directory is opaque when viewed through a union stack. |
| 131 | UF_OPAQUE; |
| 132 | /// The file is read only, and may not be written or appended. |
| 133 | #[cfg(any(target_os = "freebsd" ))] |
| 134 | UF_READONLY; |
| 135 | /// The file contains a Windows reparse point. |
| 136 | #[cfg(any(target_os = "freebsd" ))] |
| 137 | UF_REPARSE; |
| 138 | /// Mask of owner changeable flags. |
| 139 | UF_SETTABLE; |
| 140 | /// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute. |
| 141 | #[cfg(any(target_os = "freebsd" ))] |
| 142 | UF_SPARSE; |
| 143 | /// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM` |
| 144 | /// attribute. |
| 145 | #[cfg(any(target_os = "freebsd" ))] |
| 146 | UF_SYSTEM; |
| 147 | /// File renames and deletes are tracked. |
| 148 | #[cfg(apple_targets)] |
| 149 | UF_TRACKED; |
| 150 | #[cfg(any(target_os = "dragonfly" ))] |
| 151 | UF_XLINK; |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | /// Create a special or ordinary file, by pathname. |
| 156 | pub fn mknod<P: ?Sized + NixPath>( |
| 157 | path: &P, |
| 158 | kind: SFlag, |
| 159 | perm: Mode, |
| 160 | dev: dev_t, |
| 161 | ) -> Result<()> { |
| 162 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 163 | libc::mknod(pathname:cstr.as_ptr(), mode:kind.bits() | perm.bits() as mode_t, dev) |
| 164 | })?; |
| 165 | |
| 166 | Errno::result(res).map(op:drop) |
| 167 | } |
| 168 | |
| 169 | /// Create a special or ordinary file, relative to a given directory. |
| 170 | #[cfg (not(any(apple_targets, target_os = "redox" , target_os = "haiku" )))] |
| 171 | pub fn mknodat<P: ?Sized + NixPath>( |
| 172 | dirfd: Option<RawFd>, |
| 173 | path: &P, |
| 174 | kind: SFlag, |
| 175 | perm: Mode, |
| 176 | dev: dev_t, |
| 177 | ) -> Result<()> { |
| 178 | let dirfd: i32 = at_rawfd(fd:dirfd); |
| 179 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 180 | libc::mknodat( |
| 181 | dirfd, |
| 182 | pathname:cstr.as_ptr(), |
| 183 | mode:kind.bits() | perm.bits() as mode_t, |
| 184 | dev, |
| 185 | ) |
| 186 | })?; |
| 187 | |
| 188 | Errno::result(res).map(op:drop) |
| 189 | } |
| 190 | |
| 191 | #[cfg (target_os = "linux" )] |
| 192 | pub const fn major(dev: dev_t) -> u64 { |
| 193 | ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff) |
| 194 | } |
| 195 | |
| 196 | #[cfg (target_os = "linux" )] |
| 197 | pub const fn minor(dev: dev_t) -> u64 { |
| 198 | ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff) |
| 199 | } |
| 200 | |
| 201 | #[cfg (target_os = "linux" )] |
| 202 | pub const fn makedev(major: u64, minor: u64) -> dev_t { |
| 203 | ((major & 0xffff_f000) << 32) |
| 204 | | ((major & 0x0000_0fff) << 8) |
| 205 | | ((minor & 0xffff_ff00) << 12) |
| 206 | | (minor & 0x0000_00ff) |
| 207 | } |
| 208 | |
| 209 | pub fn umask(mode: Mode) -> Mode { |
| 210 | let prev: u32 = unsafe { libc::umask(mask:mode.bits() as mode_t) }; |
| 211 | Mode::from_bits(prev).expect(msg:"[BUG] umask returned invalid Mode" ) |
| 212 | } |
| 213 | |
| 214 | pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> { |
| 215 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
| 216 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 217 | libc::stat(path:cstr.as_ptr(), buf:dst.as_mut_ptr()) |
| 218 | })?; |
| 219 | |
| 220 | Errno::result(res)?; |
| 221 | |
| 222 | Ok(unsafe { dst.assume_init() }) |
| 223 | } |
| 224 | |
| 225 | pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> { |
| 226 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
| 227 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 228 | libc::lstat(path:cstr.as_ptr(), buf:dst.as_mut_ptr()) |
| 229 | })?; |
| 230 | |
| 231 | Errno::result(res)?; |
| 232 | |
| 233 | Ok(unsafe { dst.assume_init() }) |
| 234 | } |
| 235 | |
| 236 | pub fn fstat(fd: RawFd) -> Result<FileStat> { |
| 237 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
| 238 | let res: i32 = unsafe { libc::fstat(fildes:fd, buf:dst.as_mut_ptr()) }; |
| 239 | |
| 240 | Errno::result(res)?; |
| 241 | |
| 242 | Ok(unsafe { dst.assume_init() }) |
| 243 | } |
| 244 | |
| 245 | #[cfg (not(target_os = "redox" ))] |
| 246 | pub fn fstatat<P: ?Sized + NixPath>( |
| 247 | dirfd: Option<RawFd>, |
| 248 | pathname: &P, |
| 249 | f: AtFlags, |
| 250 | ) -> Result<FileStat> { |
| 251 | let dirfd: i32 = at_rawfd(fd:dirfd); |
| 252 | let mut dst: MaybeUninit = mem::MaybeUninit::uninit(); |
| 253 | let res: i32 = pathname.with_nix_path(|cstr: &CStr| unsafe { |
| 254 | libc::fstatat( |
| 255 | dirfd, |
| 256 | pathname:cstr.as_ptr(), |
| 257 | buf:dst.as_mut_ptr(), |
| 258 | flags:f.bits() as libc::c_int, |
| 259 | ) |
| 260 | })?; |
| 261 | |
| 262 | Errno::result(res)?; |
| 263 | |
| 264 | Ok(unsafe { dst.assume_init() }) |
| 265 | } |
| 266 | |
| 267 | /// Change the file permission bits of the file specified by a file descriptor. |
| 268 | /// |
| 269 | /// # References |
| 270 | /// |
| 271 | /// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html). |
| 272 | pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> { |
| 273 | let res: i32 = unsafe { libc::fchmod(fd, mode.bits() as mode_t) }; |
| 274 | |
| 275 | Errno::result(res).map(op:drop) |
| 276 | } |
| 277 | |
| 278 | /// Flags for `fchmodat` function. |
| 279 | #[derive (Clone, Copy, Debug)] |
| 280 | pub enum FchmodatFlags { |
| 281 | FollowSymlink, |
| 282 | NoFollowSymlink, |
| 283 | } |
| 284 | |
| 285 | /// Change the file permission bits. |
| 286 | /// |
| 287 | /// The file to be changed is determined relative to the directory associated |
| 288 | /// with the file descriptor `dirfd` or the current working directory |
| 289 | /// if `dirfd` is `None`. |
| 290 | /// |
| 291 | /// If `flag` is `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link, |
| 292 | /// then the mode of the symbolic link is changed. |
| 293 | /// |
| 294 | /// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to |
| 295 | /// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented |
| 296 | /// in the `nix` crate. |
| 297 | /// |
| 298 | /// # References |
| 299 | /// |
| 300 | /// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html). |
| 301 | #[cfg (not(target_os = "redox" ))] |
| 302 | pub fn fchmodat<P: ?Sized + NixPath>( |
| 303 | dirfd: Option<RawFd>, |
| 304 | path: &P, |
| 305 | mode: Mode, |
| 306 | flag: FchmodatFlags, |
| 307 | ) -> Result<()> { |
| 308 | let atflag: AtFlags = match flag { |
| 309 | FchmodatFlags::FollowSymlink => AtFlags::empty(), |
| 310 | FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, |
| 311 | }; |
| 312 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 313 | libc::fchmodat( |
| 314 | dirfd:at_rawfd(dirfd), |
| 315 | pathname:cstr.as_ptr(), |
| 316 | mode.bits() as mode_t, |
| 317 | flags:atflag.bits() as libc::c_int, |
| 318 | ) |
| 319 | })?; |
| 320 | |
| 321 | Errno::result(res).map(op:drop) |
| 322 | } |
| 323 | |
| 324 | /// Change the access and modification times of a file. |
| 325 | /// |
| 326 | /// `utimes(path, times)` is identical to |
| 327 | /// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former |
| 328 | /// is a deprecated API so prefer using the latter if the platforms you care |
| 329 | /// about support it. |
| 330 | /// |
| 331 | /// # References |
| 332 | /// |
| 333 | /// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html). |
| 334 | pub fn utimes<P: ?Sized + NixPath>( |
| 335 | path: &P, |
| 336 | atime: &TimeVal, |
| 337 | mtime: &TimeVal, |
| 338 | ) -> Result<()> { |
| 339 | let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
| 340 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 341 | libc::utimes(filename:cstr.as_ptr(), ×[0]) |
| 342 | })?; |
| 343 | |
| 344 | Errno::result(res).map(op:drop) |
| 345 | } |
| 346 | |
| 347 | /// Change the access and modification times of a file without following symlinks. |
| 348 | /// |
| 349 | /// `lutimes(path, times)` is identical to |
| 350 | /// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former |
| 351 | /// is a deprecated API so prefer using the latter if the platforms you care |
| 352 | /// about support it. |
| 353 | /// |
| 354 | /// # References |
| 355 | /// |
| 356 | /// [lutimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html). |
| 357 | #[cfg (any( |
| 358 | target_os = "linux" , |
| 359 | target_os = "haiku" , |
| 360 | apple_targets, |
| 361 | target_os = "freebsd" , |
| 362 | target_os = "netbsd" |
| 363 | ))] |
| 364 | pub fn lutimes<P: ?Sized + NixPath>( |
| 365 | path: &P, |
| 366 | atime: &TimeVal, |
| 367 | mtime: &TimeVal, |
| 368 | ) -> Result<()> { |
| 369 | let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
| 370 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 371 | libc::lutimes(file:cstr.as_ptr(), ×[0]) |
| 372 | })?; |
| 373 | |
| 374 | Errno::result(res).map(op:drop) |
| 375 | } |
| 376 | |
| 377 | /// Change the access and modification times of the file specified by a file descriptor. |
| 378 | /// |
| 379 | /// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use |
| 380 | /// `TimeSpec::UTIME_OMIT` if you don't want to change it. |
| 381 | /// |
| 382 | /// # References |
| 383 | /// |
| 384 | /// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html). |
| 385 | #[inline ] |
| 386 | pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> { |
| 387 | let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
| 388 | let res: i32 = unsafe { libc::futimens(fd, ×[0]) }; |
| 389 | |
| 390 | Errno::result(res).map(op:drop) |
| 391 | } |
| 392 | |
| 393 | /// Flags for `utimensat` function. |
| 394 | // TODO: replace with fcntl::AtFlags |
| 395 | #[derive (Clone, Copy, Debug)] |
| 396 | pub enum UtimensatFlags { |
| 397 | FollowSymlink, |
| 398 | NoFollowSymlink, |
| 399 | } |
| 400 | |
| 401 | /// Change the access and modification times of a file. |
| 402 | /// |
| 403 | /// The file to be changed is determined relative to the directory associated |
| 404 | /// with the file descriptor `dirfd` or the current working directory |
| 405 | /// if `dirfd` is `None`. |
| 406 | /// |
| 407 | /// If `flag` is `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link, |
| 408 | /// then the mode of the symbolic link is changed. |
| 409 | /// |
| 410 | /// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to |
| 411 | /// `utimes(path, times)`. The latter is a deprecated API so prefer using the |
| 412 | /// former if the platforms you care about support it. |
| 413 | /// |
| 414 | /// If you want to set the timestamp to now, use `TimeSpec::UTIME_NOW`. Use |
| 415 | /// `TimeSpec::UTIME_OMIT` if you don't want to change it. |
| 416 | /// |
| 417 | /// # References |
| 418 | /// |
| 419 | /// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html). |
| 420 | #[cfg (not(target_os = "redox" ))] |
| 421 | pub fn utimensat<P: ?Sized + NixPath>( |
| 422 | dirfd: Option<RawFd>, |
| 423 | path: &P, |
| 424 | atime: &TimeSpec, |
| 425 | mtime: &TimeSpec, |
| 426 | flag: UtimensatFlags, |
| 427 | ) -> Result<()> { |
| 428 | let atflag: AtFlags = match flag { |
| 429 | UtimensatFlags::FollowSymlink => AtFlags::empty(), |
| 430 | UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, |
| 431 | }; |
| 432 | let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; |
| 433 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 434 | libc::utimensat( |
| 435 | dirfd:at_rawfd(dirfd), |
| 436 | path:cstr.as_ptr(), |
| 437 | ×[0], |
| 438 | flag:atflag.bits() as libc::c_int, |
| 439 | ) |
| 440 | })?; |
| 441 | |
| 442 | Errno::result(res).map(op:drop) |
| 443 | } |
| 444 | |
| 445 | #[cfg (not(target_os = "redox" ))] |
| 446 | pub fn mkdirat<P: ?Sized + NixPath>( |
| 447 | fd: Option<RawFd>, |
| 448 | path: &P, |
| 449 | mode: Mode, |
| 450 | ) -> Result<()> { |
| 451 | let fd: i32 = at_rawfd(fd); |
| 452 | let res: i32 = path.with_nix_path(|cstr: &CStr| unsafe { |
| 453 | libc::mkdirat(dirfd:fd, pathname:cstr.as_ptr(), mode.bits() as mode_t) |
| 454 | })?; |
| 455 | |
| 456 | Errno::result(res).map(op:drop) |
| 457 | } |
| 458 | |