Age | Commit message (Collapse) | Author |
|
1692: Add IP_DONTFRAG and IPV6_DONTFRAG SockOpts r=rtzoeller a=junhochoi
IP_DONTFRAG: iOS, macOS
IPV6_DONTFRAG: android, iOS, linux and macOS
Test: `cargo test --test test dontfrag_opts`
Some CI tests running ENOPROTOOPT are disabled.
Co-authored-by: Junho Choi <junho.choi@gmail.com>
|
|
IP_DONTFRAG: iOS, macOS
IPV6_DONTFRAG: android, iOS, linux and macOS
Test: `cargo test --test test dontfrag_opts`
Some CI tests running ENOPROTOOPT are disabled (qemu-based).
|
|
1690: Enable statfs magic constants for target_os = "android" r=rtzoeller a=flxo
The statfs magic constants of file systems types are available on
target_os android and the cfg guard is updated accordingly.
Sync the list of constant with the constants declared in libc.
Fixes #1689
Co-authored-by: Felix Obenhuber <felix@obenhuber.de>
|
|
The statfs magic constants of file systems types are available on
target_os android and the cfg guard is updated accordingly.
Sync the list of constant with the constants declared in libc.
Fixes #1689
|
|
1691: [skip ci] edit CHANGELOG formatting prior to 0.24.0 release r=rtzoeller a=asomers
Co-authored-by: Alan Somers <asomers@gmail.com>
|
|
The latest redox-syscall crate requires at least Rust 1.59.0, but they
don't define an MSRV policy. And the version given in the
rust-toolchain file in the Redox repository doesn't work. So until they
clarify their MSRV, use nightly.
https://gitlab.redox-os.org/redox-os/syscall/-/commit/30f29c32952343412bb6c36c9fda136d26e9431f
|
|
|
|
1672: Make `uname` always safe r=asomers a=koute
Currently `uname` doesn't check for errors and just blindly assumes that it always succeeds. According to the manpage this function can fail, even though no actual errors are defined:
```
RETURN VALUE
Upon successful completion, a non-negative value shall be returned. Otherwise, -1 shall be returned and errno set to indicate the error.
ERRORS
No errors are defined.
The following sections are informative.
```
Looking at [the glibc's sources](https://github.com/bminor/glibc/blob/b92a49359f33a461db080a33940d73f47c756126/posix/uname.c#L29) we can see that it indeed could fail if the internal `gethostname` call fails for some reason.
This code also assumes that every field of `utsname` is going to be initialized by the call to `uname`, which apparently is also not true. Even though the interface doesn't expose this field so it's not a problem in practice (although it might be UB since we do call `assume_init` on the whole struct) [the `utsname` does have a `domainname` field](https://docs.rs/libc/0.2.119/libc/struct.utsname.html) which glibc doesn't initialize.
The code also assumes that every field is a valid UTF-8 string, which is also technically not guaranteed.
The code also assumes that every field will be null terminated, which might not be true if any of the strings are too long (since glibc uses `strncpy` which will *not* null-terminate the string if it ends up running out of space).
This PR should fix all of these problems.
This is a breaking change.
Co-authored-by: Jan Bujak <jan@parity.io>
|
|
1686: [skip ci] better docs for SockaddrLike::from_raw r=asomers a=asomers
Fixes #1680
Co-authored-by: Alan Somers <asomers@gmail.com>
|
|
This fixes several issues with the current `uname` bindings:
- Do not ignore `uname` errors; at least on glibc `uname` can fail,
so now it returns a `Result` instead of assuming that the call
will always succeed.
- Do not assume `uname` will initialize every member of `utsname`;
not every implementation initializes every field, so internally
the struct is now zero-initialized.
- Do not blindly assume strings returned by `uname` will always be valid UTF-8;
`UtsName`'s accessors will now return `&OsStr`s instead of `&str`s.
|
|
Fixes #1680
|
|
1685: Deprecate IpAddr, Ipv4Addr, and Ipv6Addr r=rtzoeller a=asomers
Because they're redundant with types in the standard library.
Fixes #1681
Co-authored-by: Alan Somers <asomers@gmail.com>
|
|
Because they're redundant with types in the standard library.
Fixes #1681
|
|
1684: Replace the Sockaddr enum with a union r=rtzoeller a=asomers
The SockAddr enum is quite large, and the user must allocate space for
the whole thing even though he usually knows what type he needs.
Furthermore, thanks to the sa_family field, the sockaddr types are
basically an enum even in C.
So replace the ungainly enum with a SockaddrLike trait implemented by
all sockaddr types and a SockaddrStorage union that has safe accessors.
Also, deprecate InetAddr, which only existed to support SockAddr.
Supplants #1504
Fixes #1544
Co-authored-by: Alan Somers <asomers@gmail.com>
|
|
The SockAddr enum is quite large, and the user must allocate space for
the whole thing even though he usually knows what type he needs.
Furthermore, thanks to the sa_family field, the sockaddr types are
basically an enum even in C.
So replace the ungainly enum with a SockaddrLike trait implemented by
all sockaddr types and a SockaddrStorage union that has safe accessors.
Also, deprecate InetAddr, which only existed to support SockAddr.
Supplants #1504
Fixes #1544
|
|
1682: [skip ci] spellcheck a comment for sethostname r=rtzoeller a=asomers
Co-authored-by: Alan Somers <asomers@gmail.com>
|
|
1683: Fix build for Redox and uclibc r=asomers a=rtzoeller
- Redox renamed `sigaction.sa_handler` to `.sa_sigaction`, which lets us drop some specialized code for the platform.
- uclibc now uses a `u32` for the `RLIMIT` definitions, like Linux GNU.
Co-authored-by: Ryan Zoeller <rtzoeller@rtzoeller.com>
|
|
|
|
|
|
|
|
1677: Use the same signature for LinkAddr::addr on all platforms r=rtzoeller a=asomers
This should've been done as part of #1675
Co-authored-by: Alan Somers <asomers@gmail.com>
|
|
This should've been done as part of #1675
|
|
1668: Change getrlimit and setrlimit to use rlim_t directly. r=asomers a=Arnavion
Fixes #1666
Co-authored-by: Arnavion <me@arnavion.dev>
|
|
Fixes #1666
|
|
1675: Fix a panic in Linkaddr::addr r=rtzoeller a=asomers
The function assumed something about the values of the sockaddr_dl's
fields. But because the inner type is public, we musn't do that. The
only solution is to change the function's signature to return an Option.
1676: Fix the build on DragonflyBSD with -Zminimal-versions r=asomers a=asomers
Co-authored-by: Alan Somers <asomers@gmail.com>
|
|
|
|
The function assumed something about the values of the sockaddr_dl's
fields. But because the inner type is public, we musn't do that. The
only solution is to change the function's signature to return an Option.
|
|
1671: docs: fix link in doc for cmsg_space in sys/socket r=rtzoeller a=liubin
Link to cmsg_space macro for recvmmsg and recvmsg should
be the doc's root of crate as macro is an exported macro.
Signed-off-by: bin liu <liubin0329@gmail.com>
Co-authored-by: bin liu <liubin0329@gmail.com>
|
|
1584: wait: implement waitid() r=rtzoeller a=NeoRaider
waitid() has a number of additional features that waitpid() is missing:
- WNOWAIT is only accepted for waitid() on Linux (and possibly other platforms)
- Support for waiting on PID file descriptors on Linux
For now support is added for all platforms with waitid() that have proper siginfo_t support in libc. NetBSD support is currently a work in progress [1].
Tests for the signal/exit code are currently skipped on MIPS platforms due to multiple bugs in qemu-user in the translation of siginfo_t (one fixed in January [2], one currently under review [3]).
[1] rust-lang/libc#2476
[2] https://lists.nongnu.org/archive/html/qemu-devel/2021-01/msg04810.html
[3] https://lists.nongnu.org/archive/html/qemu-devel/2021-10/msg05433.html
Co-authored-by: Matthias Schiffer <mschiffer@universe-factory.net>
|
|
waitid() has a number of additional features that waitpid() is missing:
- WNOWAIT is only accepted for waitid() on Linux (and possibly other
platforms)
- Support for waiting on PID file descriptors on Linux
For now support is added for all platforms with waitid() that have proper
siginfo_t support in libc. NetBSD support is currently a work in progress
[1].
Tests for the signal/exit code are currently skipped on MIPS platforms due
to bugs in qemu-user's translation of siginfo_t (fixed in [2] and [3]; the
second fix is not in a released qemu version yet).
[1] https://github.com/rust-lang/libc/pull/2476
[2] https://lists.nongnu.org/archive/html/qemu-devel/2021-01/msg04810.html
[3] https://lists.nongnu.org/archive/html/qemu-devel/2021-10/msg05433.html
|
|
Link to cmsg_space macro for recvmmsg and recvmsg should
be the doc's root of crate as macro is an exported macro.
Signed-off-by: bin liu <liubin0329@gmail.com>
|
|
1670: Add `MsgFlag::MSG_NOSIGNAL` r=rtzoeller a=i509VCB
(I guess I will see if I did the cfg block correctly)
Co-authored-by: i509VCB <git@i509.me>
|
|
|
|
1669: Fix complation on riscv32 r=asomers a=kraj
Signed-off-by: Khem Raj <raj.khem@gmail.com>
Co-authored-by: Khem Raj <raj.khem@gmail.com>
|
|
Upstream-Status: Pending
Signed-off-by: Khem Raj <raj.khem@gmail.com>
|
|
Signed-off-by: Khem Raj <raj.khem@gmail.com>
|
|
1656: Remove PATH_MAX restriction from with_nix_path and further improve performance r=rtzoeller a=SUPERCILEX
This PR removes the `PATH_MAX` limitation since that's wrong anyway: https://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
A nice side effect is that this lets us further optimize `with_nix_path` by having the compiler not insert probe frames, saving us another fat pile of instructions:
```
2,289,930 ( 1.67%) 305,324 ( 0.99%) 152,662 ( 0.74%) 18 ( 0.15%) . . . . . => ???:__rust_probestack (152,662x)
```
New numbers:
```
19,257,593 (16.43%) 4,415,242 (15.75%) 2,593,635 (12.82%) 41 ( 0.27%) 47 ( 0.12%) 53 ( 0.26%) 7 ( 0.12%) 0 3 ( 0.02%) 2,404,730 (14.52%) 2,721 ( 0.53%) 762,780 (30.96%) 66 ( 0.90%) => /home/asaveau/Desktop/nix/src/lib.rs:<[u8] as nix::NixPath>::with_nix_path (152,556x)
```
```
Ir Dr Dw I1mr D1mr D1mw ILmr DLmr DLmw Bc Bcm Bi Bim
1,067,892 ( 0.91%) 0 457,668 ( 2.26%) 2 ( 0.01%) 0 0 1 ( 0.02%) . . . . . . fn with_nix_path<T, F>(&self, f: F) -> Result<T>
. . . . . . . . . . . . . where
. . . . . . . . . . . . . F: FnOnce(&CStr) -> T,
. . . . . . . . . . . . . {
. . . . . . . . . . . . . // The real PATH_MAX is 4096, but it's statistically unlikely to have a path longer than
. . . . . . . . . . . . . // ~300 bytes. See the appendix to get stats for your own machine.
. . . . . . . . . . . . . //
. . . . . . . . . . . . . // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
. . . . . . . . . . . . . // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
. . . . . . . . . . . . . const MAX_STACK_ALLOCATION: usize = 512;
. . . . . . . . . . . . .
305,112 ( 0.26%) 0 0 0 0 0 0 0 0 152,556 ( 0.92%) . . . if self.len() >= MAX_STACK_ALLOCATION {
. . . . . . . . . . . . . return with_nix_path_allocating(self, f);
. . . . . . . . . . . . . }
. . . . . . . . . . . . .
. . . . . . . . . . . . . let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
. . . . . . . . . . . . . let buf_ptr = buf.as_mut_ptr() as *mut u8;
. . . . . . . . . . . . .
. . . . . . . . . . . . . unsafe {
. . . . . . . . . . . . . ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
. . . . . . . . . . . . . buf_ptr.add(self.len()).write(0);
. . . . . . . . . . . . . }
. . . . . . . . . . . . .
1,067,892 ( 0.91%) 305,112 ( 1.09%) 152,556 ( 0.75%) 2 ( 0.01%) 7 ( 0.02%) 0 1 ( 0.02%) 0 0 305,112 ( 1.84%) 6 ( 0.00%) 152,556 ( 6.19%) 1 ( 0.01%) match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, self.len() + 1) }) {
8,730,403 ( 7.45%) 1,211,383 ( 4.32%) 1,067,892 ( 5.28%) 18 ( 0.12%) 2 ( 0.00%) 0 2 ( 0.04%) 0 0 1,336,744 ( 8.07%) 609 ( 0.12%) 152,556 ( 6.19%) 62 ( 0.85%) => /rustc/21b4a9cfdcbb1e76f4b36b5c3cfd64d627285093//library/std/src/ffi/c_str.rs:std::ffi::c_str::CStr::from_bytes_with_nul (152,556x)
610,224 ( 0.52%) 610,224 ( 2.18%) . . . . . . . . . . . Ok(s) => Ok(f(s)),
. . . . . . . . . . . . . Err(_) => Err(Errno::EINVAL),
. . . . . . . . . . . . . }
762,780 ( 0.65%) 610,224 ( 2.18%) . . . . . . . . . . . }
. . . . . . . . . . . . . }
```
## Appendix
```rust
use histogram::Histogram;
use std::env;
use std::fs::{canonicalize, read_dir};
use std::path::Path;
fn main() {
let args = env::args().collect::<Vec<String>>();
let mut histogram = Histogram::new();
if args.len() == 2 {
log(&mut histogram, canonicalize(&args[1]).unwrap());
} else {
log(&mut histogram, canonicalize(".").unwrap());
}
println!(
"min={}\nmax={}\nmean={}\np50={}\np90={}\np99={}\np999={}\nstddev={}\nstdvar={}",
histogram.minimum().unwrap(),
histogram.maximum().unwrap(),
histogram.mean().unwrap(),
histogram.percentile(50.0).unwrap(),
histogram.percentile(90.0).unwrap(),
histogram.percentile(99.0).unwrap(),
histogram.percentile(99.9).unwrap(),
histogram.stddev().unwrap(),
histogram.stdvar().unwrap(),
)
}
fn log(histogram: &mut Histogram, path: impl AsRef<Path>) {
for dir in read_dir(path.as_ref()).unwrap() {
let entry = dir.unwrap();
histogram
.increment(entry.path().as_os_str().len() as u64)
.unwrap();
if entry.file_type().unwrap().is_dir() {
log(histogram, entry.path());
}
}
}
```
Co-authored-by: Alex Saveau <saveau.alexandre@gmail.com>
|
|
Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
|
|
1667: Define _POSIX_VDISABLE on Android to fix doc test r=asomers a=rtzoeller
This fixes the compilation [this](https://github.com/rtzoeller/nix/blob/1a2ee3da3026a14d42498f5ca4e4b2c1a75bc81d/src/sys/termios.rs#L27) example documentation on Android. Tested on Termux.
Co-authored-by: Ryan Zoeller <rtzoeller@rtzoeller.com>
|
|
|
|
1664: also implement Read and Write for &PtyMaster r=rtzoeller a=doy
align with std::fs::File which also does this because the underlying calls are just syscalls which are safe to run concurrently
Co-authored-by: Jesse Luehrs <doy@tozt.net>
|
|
|
|
1665: Add ENOTRECOVERABLE and EOWNERDEAD error codes on DragonFly r=asomers a=rtzoeller
Co-authored-by: Ryan Zoeller <rtzoeller@rtzoeller.com>
|
|
|
|
1553: Implement Extend and From/IntoIterator for SigSet r=rtzoeller a=magicant
Co-authored-by: WATANABE Yuki <magicant@wonderwand.net>
|
|
|
|
1655: 30x performance improvement in `with_nix_path` r=rtzoeller a=SUPERCILEX
I've been digging into CPU instructions counts and found that `nix` accounted for an eye-watering 85% of my program's instruction counts (yes, I do a lot of I/O, I know).
The fix is simple: don't initialize the stack memory since we're just going to overwrite it anyway.
> Note: I also ran rustfmt in a separate commit, not sure if that's ok.
### Before
```
650,398,225 (85.05%) 5,451,056 (17.31%) 627,714,969 (97.28%) 60 ( 0.54%) 22 ( 0.07%) 3,997 (20.98%) 10 ( 0.18%) 0 752 ( 5.98%) 627,716,244 (97.30%) 267,333 (34.62%) 914,814 (35.11%) 105 ( 0.73%) => /home/asaveau/Desktop/nix/src/lib.rs:<[u8] as nix::NixPath>::with_nix_path (152,469x)
```
```
1,677,159 ( 0.22%) 0 762,345 ( 0.12%) 14 ( 0.13%) 0 0 2 ( 0.04%) . . . . . . fn with_nix_path<T, F>(&self, f: F) -> Result<T>
2,287,035 ( 0.30%) 304,938 ( 0.97%) 152,469 ( 0.02%) 5 ( 0.04%) 0 0 0 0 0 304,938 ( 0.05%) . . . => ???:__rust_probestack (152,469x)
. . . . . . . . . . . . . where
. . . . . . . . . . . . . F: FnOnce(&CStr) -> T,
. . . . . . . . . . . . . {
457,407 ( 0.06%) 152,469 ( 0.48%) 152,469 ( 0.02%) 0 0 93 ( 0.49%) 0 0 16 ( 0.13%) 0 0 152,469 ( 5.85%) 97 ( 0.67%) let mut buf = [0u8; PATH_MAX as usize];
627,104,997 (82.00%) 304,938 ( 0.97%) 624,513,024 (96.78%) 1 ( 0.01%) 0 3,814 (20.02%) 1 ( 0.02%) 0 720 ( 5.72%) 625,122,900 (96.90%) 152,495 (19.75%) . . => ./string/../sysdeps/x86_64/multiarch/memset-vec-unaligned-erms.S:__memset_avx2_unaligned_erms (152,469x)
. . . . . . . . . . . . .
304,938 ( 0.04%) 0 0 0 0 0 0 0 0 152,469 ( 0.02%) 2 ( 0.00%) . . if self.len() >= PATH_MAX as usize {
. . . . . . . . . . . . . return Err(Errno::ENAMETOOLONG);
. . . . . . . . . . . . . }
. . . . . . . . . . . . .
1,067,283 ( 0.14%) 152,469 ( 0.48%) 152,469 ( 0.02%) 1 ( 0.01%) 4 ( 0.01%) 0 1 ( 0.02%) 0 0 0 0 152,469 ( 5.85%) 1 ( 0.01%) buf[..self.len()].copy_from_slice(self);
3,202,541 ( 0.42%) 914,814 ( 2.91%) 609,876 ( 0.09%) 1 ( 0.01%) 2 ( 0.01%) 0 0 0 0 458,005 ( 0.07%) 114,562 (14.84%) 152,469 ( 5.85%) . => /rustc/21b4a9cfdcbb1e76f4b36b5c3cfd64d627285093/library/core/src/slice/mod.rs:core::slice::<impl [T]>::copy_from_slice (152,469x)
762,345 ( 0.10%) 304,938 ( 0.97%) 152,469 ( 0.02%) 0 6 ( 0.02%) 0 0 0 0 152,469 ( 0.02%) 6 ( 0.00%) 152,469 ( 5.85%) 1 ( 0.01%) match CStr::from_bytes_with_nul(&buf[..=self.len()]) {
8,350,190 ( 1.09%) 1,181,828 ( 3.75%) 1,067,283 ( 0.17%) 19 ( 0.17%) 2 ( 0.01%) 87 ( 0.46%) 3 ( 0.05%) 0 16 ( 0.13%) 1,220,525 ( 0.19%) 167 ( 0.02%) 152,469 ( 5.85%) 5 ( 0.03%) => /rustc/21b4a9cfdcbb1e76f4b36b5c3cfd64d627285093//library/std/src/ffi/c_str.rs:std::ffi::c_str::CStr::from_bytes_with_nul (152,469x)
609,876 ( 0.08%) 609,876 ( 1.94%) 0 0 1 ( 0.00%) . . . . . . . . Ok(s) => Ok(f(s)),
. . . . . . . . . . . . . Err(_) => Err(Errno::EINVAL),
. . . . . . . . . . . . . }
914,814 ( 0.12%) 762,345 ( 2.42%) . . . . . . . . . . . }
. . . . . . . . . . . . . }
```
### After
```
21,462,416 (15.81%) 4,688,455 (15.27%) 2,896,847 (14.17%) 74 ( 0.64%) 11 ( 0.04%) 249 ( 1.58%) 8 ( 0.15%) 0 48 ( 0.38%) 2,593,200 (12.98%) 1,128 ( 0.22%) 762,305 (31.08%) 158 ( 1.32%) => /home/asaveau/Desktop/nix/src/lib.rs:<[u8] as nix::NixPath>::with_nix_path (152,461x)
```
```
1,067,227 ( 0.79%) 0 609,844 ( 2.98%) 1 ( 0.01%) 0 0 1 ( 0.02%) . . . . . . fn with_nix_path<T, F>(&self, f: F) -> Result<T>
2,286,915 ( 1.68%) 304,922 ( 0.99%) 152,461 ( 0.75%) 19 ( 0.16%) 0 0 0 0 0 304,922 ( 1.53%) . . . => ???:__rust_probestack (152,461x)
. . . . . . . . . . . . . where
. . . . . . . . . . . . . F: FnOnce(&CStr) -> T,
. . . . . . . . . . . . . {
304,922 ( 0.22%) 0 0 0 0 0 0 0 0 152,461 ( 0.76%) 6 ( 0.00%) . . if self.len() >= PATH_MAX as usize {
. . . . . . . . . . . . . return Err(Errno::ENAMETOOLONG);
. . . . . . . . . . . . . }
. . . . . . . . . . . . .
. . . . . . . . . . . . . let mut buf = MaybeUninit::<[u8; PATH_MAX as usize]>::uninit();
. . . . . . . . . . . . . let buf_ptr = buf.as_mut_ptr() as *mut u8;
. . . . . . . . . . . . .
. . . . . . . . . . . . . unsafe {
. . . . . . . . . . . . . ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
. . . . . . . . . . . . . buf_ptr.add(self.len()).write(0);
. . . . . . . . . . . . . }
. . . . . . . . . . . . .
1,067,227 ( 0.79%) 304,922 ( 0.99%) 152,461 ( 0.75%) 1 ( 0.01%) 3 ( 0.01%) 0 1 ( 0.02%) 0 0 304,922 ( 1.53%) 4 ( 0.00%) 152,461 ( 6.22%) 1 ( 0.01%) match CStr::from_bytes_with_nul(unsafe { slice::from_raw_parts(buf_ptr, self.len() + 1) }) {
8,349,726 ( 6.15%) 1,181,764 ( 3.85%) 1,067,227 ( 5.22%) 18 ( 0.15%) 1 ( 0.00%) 83 ( 0.53%) 2 ( 0.04%) 0 16 ( 0.13%) 1,220,453 ( 6.11%) 158 ( 0.03%) 152,461 ( 6.22%) 4 ( 0.03%) => /rustc/21b4a9cfdcbb1e76f4b36b5c3cfd64d627285093//library/std/src/ffi/c_str.rs:std::ffi::c_str::CStr::from_bytes_with_nul (152,461x)
609,844 ( 0.45%) 609,844 ( 1.99%) . . . . . . . . . . . Ok(s) => Ok(f(s)),
. . . . . . . . . . . . . Err(_) => Err(Errno::EINVAL),
. . . . . . . . . . . . . }
762,305 ( 0.56%) 609,844 ( 1.99%) 0 1 ( 0.01%) 0 0 1 ( 0.02%) . . . . . . }
. . . . . . . . . . . . . }
```
Co-authored-by: Alex Saveau <saveau.alexandre@gmail.com>
|
|
Signed-off-by: Alex Saveau <saveau.alexandre@gmail.com>
|
|
1662: Enable uconxtext module for s390x r=asomers a=rtzoeller
Added to libc by https://github.com/rust-lang/libc/commit/38569c719befeb5b5051caeb6b0ff628ccd0ff90
Co-authored-by: Ryan Zoeller <rtzoeller@rtzoeller.com>
|
|
Added to libc by https://github.com/rust-lang/libc/commit/38569c719befeb5b5051caeb6b0ff628ccd0ff90
|