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