os: Implement (f)pathconf

Implement pathconf and fpathconf using libc::pathconf.

os.pathconf_names is not implemented.
This commit is contained in:
Dean Li
2021-06-13 21:28:45 +08:00
parent e63d2741c3
commit d044d8ab61
3 changed files with 261 additions and 1 deletions

29
Cargo.lock generated
View File

@@ -898,6 +898,15 @@ version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d7afe4a420e3fe79967a00898cc1f4db7c8a49a9333a29f8a4bd76a253d5cd04"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.18"
@@ -2082,6 +2091,8 @@ dependencies = [
"socket2",
"sre-engine",
"static_assertions",
"strum",
"strum_macros",
"system-configuration",
"termios",
"thiserror",
@@ -2360,6 +2371,24 @@ version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "strum"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aaf86bbcfd1fa9670b7a129f64fc0c9fcbbfe4f1bc4210e9e98fe71ffc12cde2"
[[package]]
name = "strum_macros"
version = "0.21.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d06aaeeee809dbc59eb4556183dd927df67db1540de5be8d3ec0b6636358a5ec"
dependencies = [
"heck",
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "subtle"
version = "2.4.0"

View File

@@ -117,6 +117,8 @@ rustpython-common = { path = "../common" }
[target.'cfg(unix)'.dependencies]
exitcode = "1.1.2"
uname = "0.1.1"
strum = "0.21"
strum_macros = "0.21"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
gethostname = "0.2.0"

View File

@@ -10,6 +10,8 @@ use std::{env, fs};
use crate::crt_fd::Fd;
use crossbeam_utils::atomic::AtomicCell;
use num_bigint::BigInt;
#[cfg(unix)]
use strum_macros::EnumString;
use super::errno::errors;
use crate::builtins::bytes::{PyBytes, PyBytesRef};
@@ -1645,6 +1647,232 @@ mod _os {
#[pyimpl(with(PyStructSequence))]
impl UnameResult {}
enum NameOrVal {
Name(String),
Val(i32),
}
impl TryFromObject for NameOrVal {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
match obj.downcast::<int::PyInt>() {
Ok(int) => int::try_to_primitive(int.as_bigint(), vm).map(Self::Val),
Err(obj) => {
let cstring = std::ffi::CString::try_from_object(vm, obj)?;
cstring.into_string().map(Self::Name).map_err(|e| {
vm.new_os_error(format!("error while parsing string: {:?}", e))
})
}
}
}
}
// Copy from [nix::unistd::PathconfVar](https://docs.rs/nix/0.21.0/nix/unistd/enum.PathconfVar.html)
// Change enum name to fit python doc
#[cfg(unix)]
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, EnumString)]
#[repr(i32)]
#[allow(non_camel_case_types)]
pub enum PathconfVar {
#[cfg(any(
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox"
))]
/// Minimum number of bits needed to represent, as a signed integer value,
/// the maximum size of a regular file allowed in the specified directory.
PC_FILESIZEBITS = libc::_PC_FILESIZEBITS,
/// Maximum number of links to a single file.
PC_LINK_MAX = libc::_PC_LINK_MAX,
/// Maximum number of bytes in a terminal canonical input line.
PC_MAX_CANON = libc::_PC_MAX_CANON,
/// Minimum number of bytes for which space is available in a terminal input
/// queue; therefore, the maximum number of bytes a conforming application
/// may require to be typed as input before reading them.
PC_MAX_INPUT = libc::_PC_MAX_INPUT,
/// Maximum number of bytes in a filename (not including the terminating
/// null of a filename string).
PC_NAME_MAX = libc::_PC_NAME_MAX,
/// Maximum number of bytes the implementation will store as a pathname in a
/// user-supplied buffer of unspecified size, including the terminating null
/// character. Minimum number the implementation will accept as the maximum
/// number of bytes in a pathname.
PC_PATH_MAX = libc::_PC_PATH_MAX,
/// Maximum number of bytes that is guaranteed to be atomic when writing to
/// a pipe.
PC_PIPE_BUF = libc::_PC_PIPE_BUF,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
/// Symbolic links can be created.
PC_2_SYMLINKS = libc::_PC_2_SYMLINKS,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd",
target_os = "redox"
))]
/// Minimum number of bytes of storage actually allocated for any portion of
/// a file.
PC_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd"
))]
/// Recommended increment for file transfer sizes between the
/// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values.
PC_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd",
target_os = "redox"
))]
/// Maximum recommended file transfer size.
PC_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd",
target_os = "redox"
))]
/// Minimum recommended file transfer size.
PC_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "linux",
target_os = "openbsd",
target_os = "redox"
))]
/// Recommended file transfer buffer alignment.
PC_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
/// Maximum number of bytes in a symbolic link.
PC_SYMLINK_MAX = libc::_PC_SYMLINK_MAX,
/// The use of `chown` and `fchown` is restricted to a process with
/// appropriate privileges, and to changing the group ID of a file only to
/// the effective group ID of the process or to one of its supplementary
/// group IDs.
PC_CHOWN_RESTRICTED = libc::_PC_CHOWN_RESTRICTED,
/// Pathname components longer than {NAME_MAX} generate an error.
PC_NO_TRUNC = libc::_PC_NO_TRUNC,
/// This symbol shall be defined to be the value of a character that shall
/// disable terminal special character handling.
PC_VDISABLE = libc::_PC_VDISABLE,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
/// Asynchronous input or output operations may be performed for the
/// associated file.
PC_ASYNC_IO = libc::_PC_ASYNC_IO,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
/// Prioritized input or output operations may be performed for the
/// associated file.
PC_PRIO_IO = libc::_PC_PRIO_IO,
#[cfg(any(
target_os = "android",
target_os = "dragonfly",
target_os = "freebsd",
target_os = "illumos",
target_os = "linux",
target_os = "netbsd",
target_os = "openbsd",
target_os = "redox",
target_os = "solaris"
))]
/// Synchronized input or output operations may be performed for the
/// associated file.
PC_SYNC_IO = libc::_PC_SYNC_IO,
#[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
/// The resolution in nanoseconds for all file timestamps.
PC_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION,
}
#[cfg(unix)]
#[pyfunction]
fn pathconf(path: PathOrFd, name: NameOrVal, vm: &VirtualMachine) -> PyResult<Option<i64>> {
use nix::errno::{self, Errno};
use std::str::FromStr;
let name = match name {
NameOrVal::Name(s) => PathconfVar::from_str(&s)
.map_err(|_| vm.new_value_error("unrecognized configuration name".to_string()))?
as i32,
NameOrVal::Val(v) => v,
};
Errno::clear();
let raw = match path {
PathOrFd::Path(path) => {
let path = ffi::CString::new(path.into_bytes())
.map_err(|_| vm.new_value_error("embedded null character".to_owned()))?;
unsafe { libc::pathconf(path.as_ptr(), name) }
}
PathOrFd::Fd(fd) => unsafe { libc::fpathconf(fd, name) },
};
if raw == -1 {
if errno::errno() == 0 {
Ok(None)
} else {
Err(io::Error::from(Errno::last()).into_pyexception(vm))
}
} else {
Ok(Some(raw))
}
}
#[cfg(unix)]
#[pyfunction]
fn fpathconf(fd: i32, name: NameOrVal, vm: &VirtualMachine) -> PyResult<Option<i64>> {
pathconf(PathOrFd::Fd(fd), name, vm)
}
pub(super) fn support_funcs() -> Vec<SupportFunc> {
let mut supports = super::platform::support_funcs();
supports.extend(vec![
@@ -1656,7 +1884,8 @@ mod _os {
SupportFunc::new("mkdir", Some(false), Some(MKDIR_DIR_FD), Some(false)),
// mkfifo Some Some None
// mknod Some Some None
// pathconf Some None None
#[cfg(unix)]
SupportFunc::new("pathconf", Some(true), None, None),
SupportFunc::new("readlink", Some(false), None, Some(false)),
SupportFunc::new("remove", Some(false), None, Some(false)),
SupportFunc::new("unlink", Some(false), None, Some(false)),