diff --git a/Cargo.lock b/Cargo.lock index 156c88a12..385b861fa 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -104,28 +104,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8aac770f1885fd7e387acedd76065302551364496e46b3dd00860b2f8359b9d" -[[package]] -name = "backtrace" -version = "0.3.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ed64ae6d9ebfd9893193c4b2532b1292ec97bd8271c9d7d0fa90cd78a34cba" -dependencies = [ - "backtrace-sys", - "cfg-if", - "libc", - "rustc-demangle", -] - -[[package]] -name = "backtrace-sys" -version = "0.1.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7de8aba10a69c8e8d7622c5710229485ec32e9d55fdad160ea559c086fdcd118" -dependencies = [ - "cc", - "libc", -] - [[package]] name = "base64" version = "0.11.0" @@ -532,28 +510,6 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "de853764b47027c2e862a995c34978ffa63c1501f2e15f987ba11bd4f9bba193" -[[package]] -name = "failure" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8529c2421efa3066a5cbd8063d2244603824daccb6936b079010bb2aa89464b" -dependencies = [ - "backtrace", - "failure_derive", -] - -[[package]] -name = "failure_derive" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "030a733c8287d6213886dd487564ff5c8f6aae10278b3588ed177f9d18f8d231" -dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", - "synstructure", -] - [[package]] name = "fake-simd" version = "0.1.2" @@ -964,9 +920,9 @@ checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" [[package]] name = "num-bigint" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +checksum = "b7f3fc75e3697059fb1bc465e3d8cca6cf92f56854f201158b3f9c77d5a3cfa0" dependencies = [ "autocfg 1.0.0", "num-integer", @@ -987,9 +943,9 @@ dependencies = [ [[package]] name = "num-integer" -version = "0.1.42" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" dependencies = [ "autocfg 1.0.0", "num-traits", @@ -997,9 +953,9 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfb0800a0291891dd9f4fe7bd9c19384f98f7fbe0cd0f39a2c6b88b9868bbc00" +checksum = "7a6e6b7c748f995c4c29c5f5ae0248536e04a5739927c74ec0fa564805094b9f" dependencies = [ "autocfg 1.0.0", "num-integer", @@ -1008,9 +964,9 @@ dependencies = [ [[package]] name = "num-rational" -version = "0.2.4" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +checksum = "a5b4d7360f362cfb50dde8143501e6940b22f644be75a4cc90b2d81968908138" dependencies = [ "autocfg 1.0.0", "num-bigint", @@ -1020,9 +976,9 @@ dependencies = [ [[package]] name = "num-traits" -version = "0.2.11" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62be47e61d1842b9170f0fdeec8eba98e60e90e5446449a0545e5152acd7096" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" dependencies = [ "autocfg 1.0.0", ] @@ -1259,16 +1215,6 @@ dependencies = [ "unicode-xid 0.2.0", ] -[[package]] -name = "pwd" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd32d8bece608e144ca20251e714ed107cdecdabb20c2d383cfc687825106a5" -dependencies = [ - "failure", - "libc", -] - [[package]] name = "python3-sys" version = "0.2.1" @@ -1524,12 +1470,6 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "rustc-demangle" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c691c0e608126e00913e33f0ccf3727d5fc84573623b8d65b2df340b5201783" - [[package]] name = "rustc_version" version = "0.2.3" @@ -1685,7 +1625,6 @@ dependencies = [ "openssl-sys", "parking_lot", "paste", - "pwd", "rand 0.7.3", "rand_core 0.5.1", "regex", @@ -2001,18 +1940,6 @@ dependencies = [ "unicode-xid 0.2.0", ] -[[package]] -name = "synstructure" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67656ea1dc1b41b1451851562ea232ec2e5a80242139f7e679ceccfb5d61f545" -dependencies = [ - "proc-macro2 1.0.10", - "quote 1.0.3", - "syn 1.0.17", - "unicode-xid 0.2.0", -] - [[package]] name = "term" version = "0.5.2" diff --git a/Lib/test/test_pwd.py b/Lib/test/test_pwd.py new file mode 100644 index 000000000..c13a7c929 --- /dev/null +++ b/Lib/test/test_pwd.py @@ -0,0 +1,112 @@ +import sys +import unittest +from test import support + +pwd = support.import_module('pwd') + +@unittest.skipUnless(hasattr(pwd, 'getpwall'), 'Does not have getpwall()') +class PwdTest(unittest.TestCase): + + def test_values(self): + entries = pwd.getpwall() + + for e in entries: + self.assertEqual(len(e), 7) + self.assertEqual(e[0], e.pw_name) + self.assertIsInstance(e.pw_name, str) + self.assertEqual(e[1], e.pw_passwd) + self.assertIsInstance(e.pw_passwd, str) + self.assertEqual(e[2], e.pw_uid) + self.assertIsInstance(e.pw_uid, int) + self.assertEqual(e[3], e.pw_gid) + self.assertIsInstance(e.pw_gid, int) + self.assertEqual(e[4], e.pw_gecos) + self.assertIsInstance(e.pw_gecos, str) + self.assertEqual(e[5], e.pw_dir) + self.assertIsInstance(e.pw_dir, str) + self.assertEqual(e[6], e.pw_shell) + self.assertIsInstance(e.pw_shell, str) + + # The following won't work, because of duplicate entries + # for one uid + # self.assertEqual(pwd.getpwuid(e.pw_uid), e) + # instead of this collect all entries for one uid + # and check afterwards (done in test_values_extended) + + def test_values_extended(self): + entries = pwd.getpwall() + entriesbyname = {} + entriesbyuid = {} + + if len(entries) > 1000: # Huge passwd file (NIS?) -- skip this test + self.skipTest('passwd file is huge; extended test skipped') + + for e in entries: + entriesbyname.setdefault(e.pw_name, []).append(e) + entriesbyuid.setdefault(e.pw_uid, []).append(e) + + # check whether the entry returned by getpwuid() + # for each uid is among those from getpwall() for this uid + for e in entries: + if not e[0] or e[0] == '+': + continue # skip NIS entries etc. + self.assertIn(pwd.getpwnam(e.pw_name), entriesbyname[e.pw_name]) + self.assertIn(pwd.getpwuid(e.pw_uid), entriesbyuid[e.pw_uid]) + + def test_errors(self): + self.assertRaises(TypeError, pwd.getpwuid) + self.assertRaises(TypeError, pwd.getpwuid, 3.14) + self.assertRaises(TypeError, pwd.getpwnam) + self.assertRaises(TypeError, pwd.getpwnam, 42) + self.assertRaises(TypeError, pwd.getpwall, 42) + + # try to get some errors + bynames = {} + byuids = {} + for (n, p, u, g, gecos, d, s) in pwd.getpwall(): + bynames[n] = u + byuids[u] = n + + allnames = list(bynames.keys()) + namei = 0 + fakename = allnames[namei] + while fakename in bynames: + chars = list(fakename) + for i in range(len(chars)): + if chars[i] == 'z': + chars[i] = 'A' + break + elif chars[i] == 'Z': + continue + else: + chars[i] = chr(ord(chars[i]) + 1) + break + else: + namei = namei + 1 + try: + fakename = allnames[namei] + except IndexError: + # should never happen... if so, just forget it + break + fakename = ''.join(chars) + + self.assertRaises(KeyError, pwd.getpwnam, fakename) + + # In some cases, byuids isn't a complete list of all users in the + # system, so if we try to pick a value not in byuids (via a perturbing + # loop, say), pwd.getpwuid() might still be able to find data for that + # uid. Using sys.maxint may provoke the same problems, but hopefully + # it will be a more repeatable failure. + fakeuid = sys.maxsize + self.assertNotIn(fakeuid, byuids) + self.assertRaises(KeyError, pwd.getpwuid, fakeuid) + + # -1 shouldn't be a valid uid because it has a special meaning in many + # uid-related functions + self.assertRaises(KeyError, pwd.getpwuid, -1) + # should be out of uid_t range + self.assertRaises(KeyError, pwd.getpwuid, 2**128) + self.assertRaises(KeyError, pwd.getpwuid, -2**128) + +if __name__ == "__main__": + unittest.main() diff --git a/bytecode/Cargo.toml b/bytecode/Cargo.toml index 289f9fc6a..f46fd4e79 100644 --- a/bytecode/Cargo.toml +++ b/bytecode/Cargo.toml @@ -12,7 +12,7 @@ license = "MIT" bincode = "1.1" bitflags = "1.1" lz4-compress = "0.1.1" -num-bigint = { version = "0.2", features = ["serde"] } +num-bigint = { version = "0.3", features = ["serde"] } num-complex = { version = "0.2", features = ["serde"] } serde = { version = "1.0", features = ["derive"] } itertools = "0.8" diff --git a/parser/Cargo.toml b/parser/Cargo.toml index d4c0c7e7a..8d3aa4b09 100644 --- a/parser/Cargo.toml +++ b/parser/Cargo.toml @@ -14,7 +14,7 @@ lalrpop="0.17" [dependencies] lalrpop-util="0.17" log="0.4.1" -num-bigint = "0.2" +num-bigint = "0.3" num-traits = "0.2" unic-emoji-char = "0.9" unic-ucd-ident = "0.9" diff --git a/vm/Cargo.toml b/vm/Cargo.toml index a65bc2408..66c08aa9b 100644 --- a/vm/Cargo.toml +++ b/vm/Cargo.toml @@ -27,10 +27,10 @@ blake2 = "0.8" volatile = "0.2" num-complex = { version = "0.2.2", features = ["serde"] } -num-bigint = { version = "0.2.4", features = ["serde"] } +num-bigint = { version = "0.3", features = ["serde"] } num-traits = "0.2.8" num-integer = "0.1.41" -num-rational = "0.2.2" +num-rational = "0.3" num-iter = "0.1.39" rand = { version = "0.7", features = ["wasm-bindgen"] } rand_core = "0.5" @@ -92,9 +92,6 @@ unic-ucd-ident = "0.9" flame = { version = "0.2", optional = true } flamer = { version = "0.3", optional = true } -[target.'cfg(all(unix, not(any(target_os = "android", target_os = "redox"))))'.dependencies] -pwd = "1" - [target.'cfg(unix)'.dependencies] exitcode = "1.1.2" uname = "0.1.1" diff --git a/vm/src/obj/objint.rs b/vm/src/obj/objint.rs index 705bd1744..9e07ea3e6 100644 --- a/vm/src/obj/objint.rs +++ b/vm/src/obj/objint.rs @@ -124,7 +124,7 @@ fn inner_pow(int1: &BigInt, int2: &BigInt, vm: &VirtualMachine) -> PyResult { objfloat::float_pow(v1, v2, vm).into_pyobject(vm) } else { Ok(if let Some(v2) = int2.to_u64() { - vm.ctx.new_int(int1.pow(v2)) + vm.ctx.new_int(Pow::pow(int1, v2)) } else if int1.is_one() { vm.ctx.new_int(1) } else if int1.is_zero() { @@ -519,7 +519,7 @@ impl PyInt { #[pymethod(name = "__sizeof__")] fn sizeof(&self) -> usize { - size_of::() + ((self.value.bits() + 7) & !7) / 8 + size_of::() + (((self.value.bits() + 7) & !7) / 8) as usize } #[pymethod(name = "as_integer_ratio")] @@ -531,7 +531,7 @@ impl PyInt { } #[pymethod] - fn bit_length(&self) -> usize { + fn bit_length(&self) -> u64 { self.value.bits() } diff --git a/vm/src/stdlib/pwd.rs b/vm/src/stdlib/pwd.rs index 5c2872e11..a44870363 100644 --- a/vm/src/stdlib/pwd.rs +++ b/vm/src/stdlib/pwd.rs @@ -1,52 +1,54 @@ -use pwd::Passwd; - +use super::os::convert_nix_error; +use crate::obj::objint::PyIntRef; use crate::obj::objstr::PyStringRef; -use crate::obj::objtype::PyClassRef; -use crate::pyobject::{PyObjectRef, PyRef, PyResult, PyValue}; +use crate::pyobject::{PyClassImpl, PyObjectRef, PyResult}; use crate::vm::VirtualMachine; +use std::convert::TryFrom; +use std::ptr::NonNull; -impl PyValue for Passwd { - fn class(vm: &VirtualMachine) -> PyClassRef { - vm.class("pwd", "struct_passwd") +use nix::unistd::{self, User}; + +#[pystruct_sequence(name = "pwd.struct_passwd")] +struct Passwd { + pw_name: String, + pw_passwd: String, + pw_uid: u32, + pw_gid: u32, + pw_gecos: String, + pw_dir: String, + pw_shell: String, +} + +impl From for Passwd { + fn from(user: User) -> Self { + // this is just a pain... + let cstr_lossy = |s: std::ffi::CString| { + s.into_string() + .unwrap_or_else(|e| e.into_cstring().to_string_lossy().into_owned()) + }; + let pathbuf_lossy = |p: std::path::PathBuf| { + p.into_os_string() + .into_string() + .unwrap_or_else(|s| s.to_string_lossy().into_owned()) + }; + Passwd { + pw_name: user.name, + pw_passwd: cstr_lossy(user.passwd), + pw_uid: user.uid.as_raw(), + pw_gid: user.gid.as_raw(), + pw_gecos: cstr_lossy(user.gecos), + pw_dir: pathbuf_lossy(user.dir), + pw_shell: pathbuf_lossy(user.shell), + } } } -type PasswdRef = PyRef; - -impl PasswdRef { - fn pw_name(self) -> String { - self.name.clone() - } - - fn pw_passwd(self) -> Option { - self.passwd.clone() - } - - fn pw_uid(self) -> u32 { - self.uid - } - - fn pw_gid(self) -> u32 { - self.gid - } - - fn pw_gecos(self) -> Option { - self.gecos.clone() - } - - fn pw_dir(self) -> String { - self.dir.clone() - } - - fn pw_shell(self) -> String { - self.shell.clone() - } -} - -fn pwd_getpwnam(name: PyStringRef, vm: &VirtualMachine) -> PyResult { - match Passwd::from_name(name.as_str()) { - Ok(Some(passwd)) => Ok(passwd), - _ => { +fn pwd_getpwnam(name: PyStringRef, vm: &VirtualMachine) -> PyResult { + match User::from_name(name.as_str()).map_err(|e| convert_nix_error(vm, e))? { + Some(user) => Ok(Passwd::from(user) + .into_struct_sequence(vm, vm.try_class("pwd", "struct_passwd")?)? + .into_object()), + None => { let name_repr = vm.to_repr(name.as_object())?; let message = vm.new_str(format!("getpwnam(): name not found: {}", name_repr)); Err(vm.new_key_error(message)) @@ -54,32 +56,51 @@ fn pwd_getpwnam(name: PyStringRef, vm: &VirtualMachine) -> PyResult { } } -fn pwd_getpwuid(uid: u32, vm: &VirtualMachine) -> PyResult { - match Passwd::from_uid(uid) { - Some(passwd) => Ok(passwd), - _ => { - let message = vm.new_str(format!("getpwuid(): uid not found: {}", uid)); +fn pwd_getpwuid(uid: PyIntRef, vm: &VirtualMachine) -> PyResult { + let uid_t = libc::uid_t::try_from(uid.as_bigint()).map(unistd::Uid::from_raw); + let user = match uid_t { + Ok(uid) => User::from_uid(uid).map_err(|e| convert_nix_error(vm, e))?, + Err(_) => None, + }; + match user { + Some(user) => Ok(Passwd::from(user) + .into_struct_sequence(vm, vm.try_class("pwd", "struct_passwd")?)? + .into_object()), + None => { + let message = vm.new_str(format!("getpwuid(): uid not found: {}", uid.as_bigint())); Err(vm.new_key_error(message)) } } } +// TODO: maybe merge this functionality into nix? +fn pwd_getpwall(vm: &VirtualMachine) -> PyResult { + // setpwent, getpwent, etc are not thread safe. Could use fgetpwent_r, but this is easier + static GETPWALL: parking_lot::Mutex<()> = parking_lot::const_mutex(()); + let _guard = GETPWALL.lock(); + let mut list = Vec::new(); + let cls = vm.try_class("pwd", "struct_passwd")?; + + unsafe { libc::setpwent() }; + while let Some(ptr) = NonNull::new(unsafe { libc::getpwent() }) { + let user = User::from(unsafe { ptr.as_ref() }); + let passwd = Passwd::from(user) + .into_struct_sequence(vm, cls.clone())? + .into_object(); + list.push(passwd); + } + unsafe { libc::endpwent() }; + + Ok(vm.ctx.new_list(list)) +} + pub fn make_module(vm: &VirtualMachine) -> PyObjectRef { let ctx = &vm.ctx; - let passwd_type = py_class!(ctx, "struct_passwd", ctx.object(), { - "pw_name" => ctx.new_readonly_getset("pw_name", PasswdRef::pw_name), - "pw_passwd" => ctx.new_readonly_getset("pw_passwd", PasswdRef::pw_passwd), - "pw_uid" => ctx.new_readonly_getset("pw_uid", PasswdRef::pw_uid), - "pw_gid" => ctx.new_readonly_getset("pw_gid", PasswdRef::pw_gid), - "pw_gecos" => ctx.new_readonly_getset("pw_gecos", PasswdRef::pw_gecos), - "pw_dir" => ctx.new_readonly_getset("pw_dir", PasswdRef::pw_dir), - "pw_shell" => ctx.new_readonly_getset("pw_shell", PasswdRef::pw_shell), - }); - py_module!(vm, "pwd", { - "struct_passwd" => passwd_type, - "getpwnam" => ctx.new_function(pwd_getpwnam), - "getpwuid" => ctx.new_function(pwd_getpwuid), + "struct_passwd" => Passwd::make_class(ctx), + "getpwnam" => named_function!(ctx, pwd, getpwnam), + "getpwuid" => named_function!(ctx, pwd, getpwuid), + "getpwall" => named_function!(ctx, pwd, getpwall), }) }