vendor: Move to the rust-vmm vmm-sys-util package

Locked to 60fe35be but no longer dependent on liujing2 repo.

Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
This commit is contained in:
Samuel Ortiz 2019-06-04 13:23:52 +02:00
parent d5f5648b37
commit a6b7715f4b
29 changed files with 17 additions and 3505 deletions

View File

@ -6,11 +6,6 @@ git = "https://github.com/bjzhjing/linux-loader"
rev = "2b95f1e1958a2b6399b590f64344cab5b4a6d608"
replace-with = "vendor+https://github.com/bjzhjing/linux-loader"
[source."https://github.com/liujing2/vmm-sys-util"]
git = "https://github.com/liujing2/vmm-sys-util"
branch = "master"
replace-with = "vendor+https://github.com/liujing2/vmm-sys-util"
[source."https://github.com/rust-vmm/vm-memory"]
git = "https://github.com/rust-vmm/vm-memory"
rev = "281b8bd6cd2927f7a65130194b203a1c2b0ad2e3"
@ -27,9 +22,6 @@ directory = "./vendor/registry-40351f815f426200"
[source."vendor+https://github.com/bjzhjing/linux-loader"]
directory = "./vendor/git-4af8f4552cd0d200"
[source."vendor+https://github.com/liujing2/vmm-sys-util"]
directory = "./vendor/git-ccfa5f04f3f14300"
[source."vendor+https://github.com/rust-vmm/vm-memory"]
directory = "./vendor/git-89548d8276566400"

18
Cargo.lock generated
View File

@ -97,7 +97,7 @@ dependencies = [
"epoll 4.0.1 (registry+https://github.com/rust-lang/crates.io-index)",
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)",
"vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)",
"vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)",
]
[[package]]
@ -161,7 +161,7 @@ dependencies = [
name = "net_gen"
version = "0.1.0"
dependencies = [
"vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)",
"vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)",
]
[[package]]
@ -172,7 +172,7 @@ dependencies = [
"net_gen 0.1.0",
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"serde 1.0.92 (registry+https://github.com/rust-lang/crates.io-index)",
"vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)",
"vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)",
]
[[package]]
@ -192,7 +192,7 @@ dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"vm-allocator 0.1.0",
"vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)",
"vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)",
"vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)",
]
[[package]]
@ -211,7 +211,7 @@ dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
"remain 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
"vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)",
"vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)",
]
[[package]]
@ -468,7 +468,7 @@ dependencies = [
"virtio-bindings 0.1.0",
"vm-allocator 0.1.0",
"vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)",
"vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)",
"vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)",
]
[[package]]
@ -489,13 +489,13 @@ dependencies = [
"vm-allocator 0.1.0",
"vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)",
"vm-virtio 0.1.0",
"vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)",
"vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)",
]
[[package]]
name = "vmm-sys-util"
version = "0.1.0"
source = "git+https://github.com/liujing2/vmm-sys-util#700a05b942cf904fca9cf1ac3e825e58c9dc9ea9"
source = "git+https://github.com/rust-vmm/vmm-sys-util#60fe35bea0bdce8b36c6186a740878880f944bdc"
dependencies = [
"libc 0.2.58 (registry+https://github.com/rust-lang/crates.io-index)",
]
@ -574,7 +574,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
"checksum vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "05c78687fb1a80548ae3250346c3db86a80a7cdd77bda190189f2d0a0987c81a"
"checksum vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory)" = "<none>"
"checksum vm-memory 0.1.0 (git+https://github.com/rust-vmm/vm-memory?rev=281b8bd6cd2927f7a65130194b203a1c2b0ad2e3)" = "<none>"
"checksum vmm-sys-util 0.1.0 (git+https://github.com/liujing2/vmm-sys-util)" = "<none>"
"checksum vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util)" = "<none>"
"checksum vmm-sys-util 0.1.0 (git+https://github.com/rust-vmm/vmm-sys-util?rev=60fe35bea0bdce8b36c6186a740878880f944bdc)" = "<none>"
"checksum winapi 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)" = "f10e386af2b13e47c89e7236a7a14a086791a2b88ebad6df9bf42040195cf770"
"checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"

View File

@ -8,7 +8,7 @@ byteorder = ">=1.2.1"
epoll = "=4.0.1"
libc = ">=0.2.39"
vm-memory = { git = "https://github.com/rust-vmm/vm-memory" }
vmm-sys-util = { git = "https://github.com/liujing2/vmm-sys-util" }
vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }
[dev-dependencies]
tempfile = ">=3.0.2"

View File

@ -4,4 +4,4 @@ version = "0.1.0"
authors = ["The Chromium OS Authors"]
[dependencies]
vmm-sys-util = { git = "https://github.com/liujing2/vmm-sys-util" }
vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }

View File

@ -9,7 +9,7 @@ rand = ">=0.6.5"
serde = ">=1.0.27"
net_gen = { path = "../net_gen" }
vmm-sys-util = { git = "https://github.com/liujing2/vmm-sys-util" }
vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }
[dev-dependencies]
lazy_static = ">=1.1.0"

View File

@ -13,4 +13,4 @@ kvm-ioctls = "0.1.0"
libc = ">=0.2.39"
log = "*"
vm-memory = { git = "https://github.com/rust-vmm/vm-memory" }
vmm-sys-util = { git = "https://github.com/liujing2/vmm-sys-util" }
vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }

View File

@ -13,4 +13,4 @@ byteorder = "*"
libc = "*"
log = "*"
remain = "*"
vmm-sys-util = { git = "https://github.com/liujing2/vmm-sys-util" }
vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }

2
vendor/.sources vendored
View File

@ -1 +1 @@
["git-3e7c44ea7d5fd800","git-4af8f4552cd0d200","git-89548d8276566400","git-bad78e1967b13e00","git-bda1448fb2afcb00","git-ccfa5f04f3f14300","registry-40351f815f426200"]
["git-3e7c44ea7d5fd800","git-4af8f4552cd0d200","git-643dc7ede01ca600","git-89548d8276566400","git-bad78e1967b13e00","git-bda1448fb2afcb00","registry-40351f815f426200"]

View File

@ -1,75 +0,0 @@
steps:
- label: "build-gnu-x86"
commands:
- cargo build --release
retry:
automatic: false
agents:
platform: x86_64.metal
plugins:
- docker#v3.0.1:
image: "fandree/rust-vmm-dev"
always-pull: true
- label: "build-gnu-arm"
commands:
- cargo build --release
retry:
automatic: false
agents:
platform: arm.metal
plugins:
- docker#v3.0.1:
image: "fandree/rust-vmm-dev"
always-pull: true
- label: "style"
command: cargo fmt --all -- --check
retry:
automatic: false
agents:
platform: x86_64.metal
plugins:
- docker#v3.0.1:
image: "fandree/rust-vmm-dev"
always-pull: true
- label: "unittests-gnu-x86"
commands:
- cargo test
retry:
automatic: false
agents:
platform: x86_64.metal
plugins:
- docker#v3.0.1:
privileged: true
image: "fandree/rust-vmm-dev"
always-pull: true
tmpfs: [ "/tmp:exec" ]
- label: "unittests-gnu-arm"
commands:
- cargo test
retry:
automatic: false
agents:
platform: arm.metal
plugins:
- docker#v3.0.1:
privileged: true
image: "fandree/rust-vmm-dev"
always-pull: true
tmpfs: [ "/tmp:exec" ]
- label: "clippy-x86"
commands:
- cargo clippy --all -- -D warnings
retry:
automatic: false
agents:
platform: x86_64.metal
plugins:
- docker#v3.0.1:
image: "fandree/rust-vmm-dev"
always-pull: true

View File

@ -1 +0,0 @@
{"files":{".buildkite/pipeline.yml":"0194df2d3534e3c6938ba2945e05696a12b8ed63d4941b3a4173c5bcf6e587c4","Cargo.toml":"d19b52349ada25dbbae4e2bea4850cf1b09ea546d5efee03ddfbbf9f184c0c72","LICENSE-APACHE":"c71d239df91726fc519c6eb72d318ec65820627232b2f796219e87dcf35d0ab4","LICENSE-BSD-3-Clause":"a6d3ebd1c2f37d4fd83d0676621f695fc0cc2d8c6e646cdbb831b46e0650c208","README.md":"94512ed84e8e89faba431d9107e85d229363c187705ab9244f0ab5269b5961b5","src/errno.rs":"b93845c200fc596b0ef2414671d965cf5c4fdbf6bba6e5e5f0d032a32c73f8ac","src/eventfd.rs":"7bd871242f49c14d8783714e6814456f51a9c280dcadf1625e1bd2313d2b5f7f","src/file_traits.rs":"398c529e7ebce143ecb9f9bd2f5f47ea3e953ac34cc211ad71cdcf1898cc7d38","src/ioctl.rs":"5c4abf75e7b6786e7da3191ac1e4460e1ec7d073a53331a6d9597bb9ccc3f88a","src/lib.rs":"ee0818e0ca6fdc340c52d514eeb2e3aeb4f7ba8e4e522bb946cdbce4779926f1","src/poll.rs":"1498c14ba022ede57c4faf17bee49cf5ac9d1c8d3883db441697ee224dac7818","src/seek_hole.rs":"de43f21bc2c5d9eb7f06e21e3c20f93476bf6016e4d041df71a02b9e54b3c3ca","src/signal.rs":"724f679cb62d268a5ec8f0704a8d6b01882f302f508c189e82054657ed8b31bf","src/syslog.rs":"fbf4bde16b1059b5b39c5318e8bb918dc431e8e0ccbc82c0d765b9ce4a8d5f96","src/tempdir.rs":"4993460e81f7df6398e0f2b07cc3d81e728aa7e0559c7f3d83b6df1876bc3776","src/terminal.rs":"85efb1df641730fa1981bac6fd65bd75f7d532bb8680a56e94d6d006eeb363e9","src/timerfd.rs":"fd3c52e3918d881c16cb1498f8f66253ee758275a6a66ed8eb11c78e69f69e55","src/write_zeroes.rs":"c2951bbdb3ab07727eda29e9a91a51e427fdf6fed0b611ea6a3732edbd9a1246"},"package":null}

View File

@ -1,9 +0,0 @@
[package]
name = "vmm-sys-util"
version = "0.1.0"
authors = ["Jing Liu <jing2.liu@linux.intel.com>"]
license = "Apache-2.0"
[dependencies]
libc = ">=0.2.39"

View File

@ -1,201 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,27 +0,0 @@
// Copyright 2017 The Chromium OS Authors. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -1,2 +0,0 @@
# vmm-sys-util
This crate is a collection of modules that provides helpers and utilities.

View File

@ -1,80 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use std::fmt::{Display, Formatter};
use std::io;
use std::result;
use libc::__errno_location;
/// An error number, retrieved from [`errno`](http://man7.org/linux/man-pages/man3/errno.3.html),
/// set by a libc function that returned an error.
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct Error(i32);
pub type Result<T> = result::Result<T, Error>;
impl Error {
/// Constructs a new error with the given `errno`.
pub fn new(e: i32) -> Error {
Error(e)
}
/// Constructs an error from the current `errno`.
///
/// The result of this only has any meaning just after a libc call that returned a value
/// indicating `errno` was set.
pub fn last() -> Error {
Error(unsafe { *__errno_location() })
}
/// Gets the `errno` for this error.
pub fn errno(self) -> i32 {
self.0
}
}
impl Display for Error {
fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
io::Error::from_raw_os_error(self.0).fmt(f)
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::new(e.raw_os_error().unwrap_or_default())
}
}
/// Returns the last `errno` as a [`Result`] that is always an error.
///
/// [`Result`]: type.Result.html
pub fn errno_result<T>() -> Result<T> {
Err(Error::last())
}
#[cfg(test)]
mod tests {
use super::*;
use libc;
use std::fs::File;
use std::io::{self, Write};
use std::os::unix::io::FromRawFd;
#[test]
pub fn test_invalid_fd() {
let mut file = unsafe { File::from_raw_fd(-1) };
assert!(file.write(b"test").is_err());
let last_err = errno_result::<i32>().unwrap_err();
assert_eq!(last_err, Error::new(libc::EBADF));
assert_eq!(last_err.errno(), libc::EBADF);
assert_eq!(last_err, Error::from(io::Error::last_os_error()));
assert_eq!(last_err, Error::last());
}
}

View File

@ -1,150 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use std::fs::File;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::{io, mem, result};
use libc::{c_void, dup, eventfd, read, write};
/// A safe wrapper around a Linux eventfd (man 2 eventfd).
pub struct EventFd {
eventfd: File,
}
impl EventFd {
/// Creates a new blocking EventFd with an initial value.
///
/// `flag`: The initial value. Refer to Linux eventfd(2).
pub fn new(flag: i32) -> result::Result<EventFd, io::Error> {
// This is safe because eventfd merely allocated an eventfd for
// our process and we handle the error case.
let ret = unsafe { eventfd(0, flag) };
if ret < 0 {
Err(io::Error::last_os_error())
} else {
// This is safe because we checked ret for success and know
// the kernel gave us an fd that we own.
Ok(EventFd {
eventfd: unsafe { File::from_raw_fd(ret) },
})
}
}
/// Adds `v` to the eventfd's count, does not block if the result will overflow the count
pub fn write(&self, v: u64) -> result::Result<(), io::Error> {
// This is safe because we made this fd and the pointer we pass
// can not overflow because we give the syscall's size parameter properly.
let ret = unsafe {
write(
self.as_raw_fd(),
&v as *const u64 as *const c_void,
mem::size_of::<u64>(),
)
};
if ret <= 0 {
Err(io::Error::last_os_error())
} else {
Ok(())
}
}
/// Tries to read from the eventfd, does not block if the counter is zero
pub fn read(&self) -> result::Result<u64, io::Error> {
let mut buf: u64 = 0;
let ret = unsafe {
// This is safe because we made this fd and the pointer we
// pass can not overflow because we give the syscall's size parameter properly.
read(
self.as_raw_fd(),
&mut buf as *mut u64 as *mut c_void,
mem::size_of::<u64>(),
)
};
if ret < 0 {
Err(io::Error::last_os_error())
} else {
Ok(buf)
}
}
/// Clones this EventFd, internally creating a new file descriptor. The new EventFd will share
/// the same underlying count within the kernel.
pub fn try_clone(&self) -> result::Result<EventFd, io::Error> {
// This is safe because we made this fd and properly check that it returns without error.
let ret = unsafe { dup(self.as_raw_fd()) };
if ret < 0 {
Err(io::Error::last_os_error())
} else {
// This is safe because we checked ret for success and know the kernel gave us an fd that we
// own.
Ok(EventFd {
eventfd: unsafe { File::from_raw_fd(ret) },
})
}
}
}
impl AsRawFd for EventFd {
fn as_raw_fd(&self) -> RawFd {
self.eventfd.as_raw_fd()
}
}
impl FromRawFd for EventFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
EventFd {
eventfd: File::from_raw_fd(fd),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use libc::EFD_NONBLOCK;
#[test]
fn test_new() {
EventFd::new(EFD_NONBLOCK).unwrap();
EventFd::new(0).unwrap();
}
#[test]
fn test_read_write() {
let evt = EventFd::new(EFD_NONBLOCK).unwrap();
evt.write(55).unwrap();
assert_eq!(evt.read().unwrap(), 55);
}
#[test]
fn test_write_overflow() {
let evt = EventFd::new(EFD_NONBLOCK).unwrap();
evt.write(std::u64::MAX - 1).unwrap();
let r = evt.write(1);
match r {
Err(ref inner) if inner.kind() == io::ErrorKind::WouldBlock => (),
_ => panic!("Unexpected"),
}
}
#[test]
fn test_read_nothing() {
let evt = EventFd::new(EFD_NONBLOCK).unwrap();
let r = evt.read();
match r {
Err(ref inner) if inner.kind() == io::ErrorKind::WouldBlock => (),
_ => panic!("Unexpected"),
}
}
#[test]
fn test_clone() {
let evt = EventFd::new(EFD_NONBLOCK).unwrap();
let evt_clone = evt.try_clone().unwrap();
evt.write(923).unwrap();
assert_eq!(evt_clone.read().unwrap(), 923);
}
}

View File

@ -1,39 +0,0 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: BSD-3-Clause
use std::fs::File;
use std::io::Result;
/// A trait for flushing the contents of a file to disk.
/// This is equivalent to File's `sync_all` method, but
/// wrapped in a trait so that it can be implemented for
/// other types.
pub trait FileSync {
// Flush buffers related to this file to disk.
fn fsync(&mut self) -> Result<()>;
}
impl FileSync for File {
fn fsync(&mut self) -> Result<()> {
self.sync_all()
}
}
/// A trait for setting the size of a file.
/// This is equivalent to File's `set_len` method, but
/// wrapped in a trait so that it can be implemented for
/// other types.
pub trait FileSetLen {
// Set the size of this file.
// This is the moral equivalent of `ftruncate()`.
fn set_len(&self, _len: u64) -> Result<()>;
}
impl FileSetLen for File {
fn set_len(&self, len: u64) -> Result<()> {
File::set_len(self, len)
}
}

View File

@ -1,225 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//! Macros and wrapper functions for dealing with ioctls.
use libc;
use std::os::raw::{c_int, c_uint, c_ulong, c_void};
use std::os::unix::io::AsRawFd;
/// Raw macro to declare the expression that calculates an ioctl number
#[macro_export]
macro_rules! ioctl_expr {
($dir:expr, $ty:expr, $nr:expr, $size:expr) => {
(($dir << $crate::ioctl::_IOC_DIRSHIFT)
| ($ty << $crate::ioctl::_IOC_TYPESHIFT)
| ($nr << $crate::ioctl::_IOC_NRSHIFT)
| ($size << $crate::ioctl::_IOC_SIZESHIFT)) as ::std::os::raw::c_ulong
};
}
/// Raw macro to declare a function that returns an ioctl number.
#[macro_export]
macro_rules! ioctl_ioc_nr {
($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr) => {
#[allow(non_snake_case)]
#[allow(clippy::cast_lossless)]
pub fn $name() -> ::std::os::raw::c_ulong {
ioctl_expr!($dir, $ty, $nr, $size)
}
};
($name:ident, $dir:expr, $ty:expr, $nr:expr, $size:expr, $($v:ident),+) => {
#[allow(non_snake_case)]
#[allow(clippy::cast_lossless)]
pub fn $name($($v: ::std::os::raw::c_uint),+) -> ::std::os::raw::c_ulong {
ioctl_expr!($dir, $ty, $nr, $size)
}
};
}
/// Declare an ioctl that transfers no data.
#[macro_export]
macro_rules! ioctl_io_nr {
($name:ident, $ty:expr, $nr:expr) => {
ioctl_ioc_nr!($name, $crate::ioctl::_IOC_NONE, $ty, $nr, 0);
};
($name:ident, $ty:expr, $nr:expr, $($v:ident),+) => {
ioctl_ioc_nr!($name, $crate::ioctl::_IOC_NONE, $ty, $nr, 0, $($v),+);
};
}
/// Declare an ioctl that reads data.
#[macro_export]
macro_rules! ioctl_ior_nr {
($name:ident, $ty:expr, $nr:expr, $size:ty) => {
ioctl_ioc_nr!(
$name,
$crate::ioctl::_IOC_READ,
$ty,
$nr,
::std::mem::size_of::<$size>() as u32
);
};
($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
ioctl_ioc_nr!(
$name,
$crate::ioctl::_IOC_READ,
$ty,
$nr,
::std::mem::size_of::<$size>() as u32,
$($v),+
);
};
}
/// Declare an ioctl that writes data.
#[macro_export]
macro_rules! ioctl_iow_nr {
($name:ident, $ty:expr, $nr:expr, $size:ty) => {
ioctl_ioc_nr!(
$name,
$crate::ioctl::_IOC_WRITE,
$ty,
$nr,
::std::mem::size_of::<$size>() as u32
);
};
($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
ioctl_ioc_nr!(
$name,
$crate::ioctl::_IOC_WRITE,
$ty,
$nr,
::std::mem::size_of::<$size>() as u32,
$($v),+
);
};
}
/// Declare an ioctl that reads and writes data.
#[macro_export]
macro_rules! ioctl_iowr_nr {
($name:ident, $ty:expr, $nr:expr, $size:ty) => {
ioctl_ioc_nr!(
$name,
$crate::ioctl::_IOC_READ | $crate::ioctl::_IOC_WRITE,
$ty,
$nr,
::std::mem::size_of::<$size>() as u32
);
};
($name:ident, $ty:expr, $nr:expr, $size:ty, $($v:ident),+) => {
ioctl_ioc_nr!(
$name,
$crate::ioctl::_IOC_READ | $crate::ioctl::_IOC_WRITE,
$ty,
$nr,
::std::mem::size_of::<$size>() as u32,
$($v),+
);
};
}
pub const _IOC_NRBITS: c_uint = 8;
pub const _IOC_TYPEBITS: c_uint = 8;
pub const _IOC_SIZEBITS: c_uint = 14;
pub const _IOC_DIRBITS: c_uint = 2;
pub const _IOC_NRMASK: c_uint = 255;
pub const _IOC_TYPEMASK: c_uint = 255;
pub const _IOC_SIZEMASK: c_uint = 16383;
pub const _IOC_DIRMASK: c_uint = 3;
pub const _IOC_NRSHIFT: c_uint = 0;
pub const _IOC_TYPESHIFT: c_uint = 8;
pub const _IOC_SIZESHIFT: c_uint = 16;
pub const _IOC_DIRSHIFT: c_uint = 30;
pub const _IOC_NONE: c_uint = 0;
pub const _IOC_WRITE: c_uint = 1;
pub const _IOC_READ: c_uint = 2;
pub const IOC_IN: c_uint = 1_073_741_824;
pub const IOC_OUT: c_uint = 2_147_483_648;
pub const IOC_INOUT: c_uint = 3_221_225_472;
pub const IOCSIZE_MASK: c_uint = 1_073_676_288;
pub const IOCSIZE_SHIFT: c_uint = 16;
// The type of the `req` parameter is different for the `musl` library. This will enable
// successful build for other non-musl libraries.
#[cfg(target_env = "musl")]
type IoctlRequest = c_int;
#[cfg(not(target_env = "musl"))]
type IoctlRequest = c_ulong;
/// Run an ioctl with no arguments.
pub unsafe fn ioctl<F: AsRawFd>(fd: &F, req: c_ulong) -> c_int {
libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, 0)
}
/// Run an ioctl with a single value argument.
pub unsafe fn ioctl_with_val<F: AsRawFd>(fd: &F, req: c_ulong, arg: c_ulong) -> c_int {
libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg)
}
/// Run an ioctl with an immutable reference.
pub unsafe fn ioctl_with_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &T) -> c_int {
libc::ioctl(
fd.as_raw_fd(),
req as IoctlRequest,
arg as *const T as *const c_void,
)
}
/// Run an ioctl with a mutable reference.
pub unsafe fn ioctl_with_mut_ref<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: &mut T) -> c_int {
libc::ioctl(
fd.as_raw_fd(),
req as IoctlRequest,
arg as *mut T as *mut c_void,
)
}
/// Run an ioctl with a raw pointer.
pub unsafe fn ioctl_with_ptr<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: *const T) -> c_int {
libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *const c_void)
}
/// Run an ioctl with a mutable raw pointer.
pub unsafe fn ioctl_with_mut_ptr<F: AsRawFd, T>(fd: &F, req: c_ulong, arg: *mut T) -> c_int {
libc::ioctl(fd.as_raw_fd(), req as IoctlRequest, arg as *mut c_void)
}
#[cfg(test)]
mod tests {
const TUNTAP: ::std::os::raw::c_uint = 0x54;
const VHOST: ::std::os::raw::c_uint = 0xAF;
const EVDEV: ::std::os::raw::c_uint = 0x45;
const KVMIO: ::std::os::raw::c_uint = 0xAE;
ioctl_io_nr!(KVM_CREATE_VM, KVMIO, 0x01);
ioctl_ior_nr!(TUNGETFEATURES, TUNTAP, 0xcf, ::std::os::raw::c_uint);
ioctl_iow_nr!(TUNSETQUEUE, TUNTAP, 0xd9, ::std::os::raw::c_int);
ioctl_io_nr!(VHOST_SET_OWNER, VHOST, 0x01);
ioctl_iowr_nr!(VHOST_GET_VRING_BASE, VHOST, 0x12, ::std::os::raw::c_int);
ioctl_iowr_nr!(KVM_GET_MSR_INDEX_LIST, KVMIO, 0x2, ::std::os::raw::c_int);
ioctl_ior_nr!(EVIOCGBIT, EVDEV, 0x20 + evt, [u8; 128], evt);
ioctl_io_nr!(FAKE_IOCTL_2_ARG, EVDEV, 0x01 + x + y, x, y);
#[test]
fn test_ioctl_macros() {
assert_eq!(0x0000_AE01, KVM_CREATE_VM());
assert_eq!(0x0000_AF01, VHOST_SET_OWNER());
assert_eq!(0x8004_54CF, TUNGETFEATURES());
assert_eq!(0x4004_54D9, TUNSETQUEUE());
assert_eq!(0xC004_AE02, KVM_GET_MSR_INDEX_LIST());
assert_eq!(0xC004_AF12, VHOST_GET_VRING_BASE());
assert_eq!(0x8080_4522, EVIOCGBIT(2));
assert_eq!(0x0000_4509, FAKE_IOCTL_2_ARG(3, 5));
}
}

View File

@ -1,78 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
extern crate libc;
mod tempdir;
#[macro_use]
pub mod ioctl;
pub mod errno;
pub mod eventfd;
pub mod file_traits;
pub mod seek_hole;
pub mod signal;
pub mod terminal;
pub mod timerfd;
pub mod write_zeroes;
#[macro_use]
pub mod syslog;
pub mod poll;
pub use crate::tempdir::*;
pub use errno::*;
pub use eventfd::*;
pub use poll::*;
use std::os::unix::io::AsRawFd;
pub use crate::file_traits::{FileSetLen, FileSync};
pub use crate::seek_hole::SeekHole;
pub use crate::write_zeroes::{PunchHole, WriteZeroes};
pub enum FallocateMode {
PunchHole,
ZeroRange,
}
/// Safe wrapper for `fallocate()`.
pub fn fallocate(
file: &dyn AsRawFd,
mode: FallocateMode,
keep_size: bool,
offset: u64,
len: u64,
) -> Result<()> {
let offset = if offset > libc::off64_t::max_value() as u64 {
return Err(Error::new(libc::EINVAL));
} else {
offset as libc::off64_t
};
let len = if len > libc::off64_t::max_value() as u64 {
return Err(Error::new(libc::EINVAL));
} else {
len as libc::off64_t
};
let mut mode = match mode {
FallocateMode::PunchHole => libc::FALLOC_FL_PUNCH_HOLE,
FallocateMode::ZeroRange => libc::FALLOC_FL_ZERO_RANGE,
};
if keep_size {
mode |= libc::FALLOC_FL_KEEP_SIZE;
}
// Safe since we pass in a valid fd and fallocate mode, validate offset and len,
// and check the return value.
let ret = unsafe { libc::fallocate64(file.as_raw_fd(), mode, offset, len) };
if ret < 0 {
errno_result()
} else {
Ok(())
}
}

View File

@ -1,711 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use std::cell::{Cell, Ref, RefCell};
use std::cmp::min;
use std::fs::File;
use std::i32;
use std::i64;
use std::marker::PhantomData;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::ptr::null_mut;
use std::slice;
use std::thread;
use std::time::Duration;
use libc::{
c_int, epoll_create1, epoll_ctl, epoll_event, epoll_wait, EINTR, EPOLLHUP, EPOLLIN, EPOLLOUT,
EPOLL_CLOEXEC, EPOLL_CTL_ADD, EPOLL_CTL_DEL, EPOLL_CTL_MOD,
};
use crate::{errno_result, Error, Result};
macro_rules! handle_eintr_errno {
($x:expr) => {{
let mut res;
loop {
res = $x;
if res != -1 || Error::last() != Error::new(EINTR) {
break;
}
}
res
}};
}
const POLL_CONTEXT_MAX_EVENTS: usize = 16;
/// EpollEvents wraps raw epoll_events, it should only be used with EpollContext.
pub struct EpollEvents(RefCell<[epoll_event; POLL_CONTEXT_MAX_EVENTS]>);
impl EpollEvents {
pub fn new() -> EpollEvents {
EpollEvents(RefCell::new(
[epoll_event { events: 0, u64: 0 }; POLL_CONTEXT_MAX_EVENTS],
))
}
}
impl Default for EpollEvents {
fn default() -> Self {
Self::new()
}
}
/// Trait for a token that can be associated with an `fd` in a `PollContext`.
///
/// Simple enums that have no or primitive variant data can use the `#[derive(PollToken)]`
/// custom derive to implement this trait.
pub trait PollToken {
/// Converts this token into a u64 that can be turned back into a token via `from_raw_token`.
fn as_raw_token(&self) -> u64;
/// Converts a raw token as returned from `as_raw_token` back into a token.
///
/// It is invalid to give a raw token that was not returned via `as_raw_token` from the same
/// `Self`. The implementation can expect that this will never happen as a result of its usage
/// in `PollContext`.
fn from_raw_token(data: u64) -> Self;
}
impl PollToken for usize {
fn as_raw_token(&self) -> u64 {
*self as u64
}
fn from_raw_token(data: u64) -> Self {
data as Self
}
}
impl PollToken for u64 {
fn as_raw_token(&self) -> u64 {
*self as u64
}
fn from_raw_token(data: u64) -> Self {
data as Self
}
}
impl PollToken for u32 {
fn as_raw_token(&self) -> u64 {
u64::from(*self)
}
fn from_raw_token(data: u64) -> Self {
data as Self
}
}
impl PollToken for u16 {
fn as_raw_token(&self) -> u64 {
u64::from(*self)
}
fn from_raw_token(data: u64) -> Self {
data as Self
}
}
impl PollToken for u8 {
fn as_raw_token(&self) -> u64 {
u64::from(*self)
}
fn from_raw_token(data: u64) -> Self {
data as Self
}
}
impl PollToken for () {
fn as_raw_token(&self) -> u64 {
0
}
fn from_raw_token(_data: u64) -> Self {}
}
/// An event returned by `PollContext::wait`.
pub struct PollEvent<'a, T> {
event: &'a epoll_event,
token: PhantomData<T>, // Needed to satisfy usage of T
}
impl<'a, T: PollToken> PollEvent<'a, T> {
/// Gets the token associated in `PollContext::add` with this event.
pub fn token(&self) -> T {
T::from_raw_token(self.event.u64)
}
/// True if the `fd` associated with this token in `PollContext::add` is readable.
pub fn readable(&self) -> bool {
self.event.events & (EPOLLIN as u32) != 0
}
/// True if the `fd` associated with this token in `PollContext::add` has been hungup on.
pub fn hungup(&self) -> bool {
self.event.events & (EPOLLHUP as u32) != 0
}
}
/// An iterator over some (sub)set of events returned by `PollContext::wait`.
pub struct PollEventIter<'a, I, T>
where
I: Iterator<Item = &'a epoll_event>,
{
mask: u32,
iter: I,
tokens: PhantomData<[T]>, // Needed to satisfy usage of T
}
impl<'a, I, T> Iterator for PollEventIter<'a, I, T>
where
I: Iterator<Item = &'a epoll_event>,
T: PollToken,
{
type Item = PollEvent<'a, T>;
fn next(&mut self) -> Option<Self::Item> {
let mask = self.mask;
self.iter
.find(|event| (event.events & mask) != 0)
.map(|event| PollEvent {
event,
token: PhantomData,
})
}
}
/// The list of event returned by `PollContext::wait`.
pub struct PollEvents<'a, T> {
count: usize,
events: Ref<'a, [epoll_event; POLL_CONTEXT_MAX_EVENTS]>,
tokens: PhantomData<[T]>, // Needed to satisfy usage of T
}
impl<'a, T: PollToken> PollEvents<'a, T> {
/// Copies the events to an owned structure so the reference to this (and by extension
/// `PollContext`) can be dropped.
pub fn to_owned(&self) -> PollEventsOwned<T> {
PollEventsOwned {
count: self.count,
events: RefCell::new(*self.events),
tokens: PhantomData,
}
}
/// Iterates over each event.
pub fn iter(&self) -> PollEventIter<slice::Iter<epoll_event>, T> {
PollEventIter {
mask: 0xffff_ffff,
iter: self.events[..self.count].iter(),
tokens: PhantomData,
}
}
/// Iterates over each readable event.
pub fn iter_readable(&self) -> PollEventIter<slice::Iter<epoll_event>, T> {
PollEventIter {
mask: EPOLLIN as u32,
iter: self.events[..self.count].iter(),
tokens: PhantomData,
}
}
/// Iterates over each hungup event.
pub fn iter_hungup(&self) -> PollEventIter<slice::Iter<epoll_event>, T> {
PollEventIter {
mask: EPOLLHUP as u32,
iter: self.events[..self.count].iter(),
tokens: PhantomData,
}
}
}
/// A deep copy of the event records from `PollEvents`.
pub struct PollEventsOwned<T> {
count: usize,
events: RefCell<[epoll_event; POLL_CONTEXT_MAX_EVENTS]>,
tokens: PhantomData<T>, // Needed to satisfy usage of T
}
impl<T: PollToken> PollEventsOwned<T> {
/// Takes a reference to the events so that they can be iterated via methods in `PollEvents`.
pub fn as_ref(&self) -> PollEvents<T> {
PollEvents {
count: self.count,
events: self.events.borrow(),
tokens: PhantomData,
}
}
}
/// Watching events taken by PollContext.
pub struct WatchingEvents(u32);
impl WatchingEvents {
/// Returns empty Events.
#[inline(always)]
pub fn empty() -> WatchingEvents {
WatchingEvents(0)
}
/// Build Events from raw epoll events (defined in epoll_ctl(2)).
#[inline(always)]
pub fn new(raw: u32) -> WatchingEvents {
WatchingEvents(raw)
}
/// Set read events.
#[inline(always)]
pub fn set_read(self) -> WatchingEvents {
WatchingEvents(self.0 | EPOLLIN as u32)
}
/// Set write events.
#[inline(always)]
pub fn set_write(self) -> WatchingEvents {
WatchingEvents(self.0 | EPOLLOUT as u32)
}
/// Get the underlying epoll events.
pub fn get_raw(&self) -> u32 {
self.0
}
}
/// EpollContext wraps linux epoll. It provides similar interface to PollContext.
/// It is thread safe while PollContext is not. It requires user to pass in a reference of
/// EpollEvents while PollContext does not. Always use PollContext if you don't need to access the
/// same epoll from different threads.
pub struct EpollContext<T> {
epoll_ctx: File,
// Needed to satisfy usage of T
tokens: PhantomData<[T]>,
}
impl<T: PollToken> EpollContext<T> {
/// Creates a new `EpollContext`.
pub fn new() -> Result<EpollContext<T>> {
// Safe because we check the return value.
let epoll_fd = unsafe { epoll_create1(EPOLL_CLOEXEC) };
if epoll_fd < 0 {
return errno_result();
}
Ok(EpollContext {
epoll_ctx: unsafe { File::from_raw_fd(epoll_fd) },
tokens: PhantomData,
})
}
/// Adds the given `fd` to this context and associates the given `token` with the `fd`'s
/// readable events.
///
/// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
/// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
/// FD number) added to this context, events will not be reported by `wait` anymore.
pub fn add(&self, fd: &AsRawFd, token: T) -> Result<()> {
self.add_fd_with_events(fd, WatchingEvents::empty().set_read(), token)
}
/// Adds the given `fd` to this context, watching for the specified events and associates the
/// given 'token' with those events.
///
/// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
/// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
/// FD number) added to this context, events will not be reported by `wait` anymore.
pub fn add_fd_with_events(&self, fd: &AsRawFd, events: WatchingEvents, token: T) -> Result<()> {
let mut evt = epoll_event {
events: events.get_raw(),
u64: token.as_raw_token(),
};
// Safe because we give a valid epoll FD and FD to watch, as well as a valid epoll_event
// structure. Then we check the return value.
let ret = unsafe {
epoll_ctl(
self.epoll_ctx.as_raw_fd(),
EPOLL_CTL_ADD,
fd.as_raw_fd(),
&mut evt,
)
};
if ret < 0 {
return errno_result();
};
Ok(())
}
/// If `fd` was previously added to this context, the watched events will be replaced with
/// `events` and the token associated with it will be replaced with the given `token`.
pub fn modify(&self, fd: &AsRawFd, events: WatchingEvents, token: T) -> Result<()> {
let mut evt = epoll_event {
events: events.0,
u64: token.as_raw_token(),
};
// Safe because we give a valid epoll FD and FD to modify, as well as a valid epoll_event
// structure. Then we check the return value.
let ret = unsafe {
epoll_ctl(
self.epoll_ctx.as_raw_fd(),
EPOLL_CTL_MOD,
fd.as_raw_fd(),
&mut evt,
)
};
if ret < 0 {
return errno_result();
};
Ok(())
}
/// Deletes the given `fd` from this context.
///
/// If an `fd`'s token shows up in the list of hangup events, it should be removed using this
/// method or by closing/dropping (if and only if the fd was never dup()'d/fork()'d) the `fd`.
/// Failure to do so will cause the `wait` method to always return immediately, causing ~100%
/// CPU load.
pub fn delete(&self, fd: &AsRawFd) -> Result<()> {
// Safe because we give a valid epoll FD and FD to stop watching. Then we check the return
// value.
let ret = unsafe {
epoll_ctl(
self.epoll_ctx.as_raw_fd(),
EPOLL_CTL_DEL,
fd.as_raw_fd(),
null_mut(),
)
};
if ret < 0 {
return errno_result();
};
Ok(())
}
/// Waits for any events to occur in FDs that were previously added to this context.
///
/// The events are level-triggered, meaning that if any events are unhandled (i.e. not reading
/// for readable events and not closing for hungup events), subsequent calls to `wait` will
/// return immediately. The consequence of not handling an event perpetually while calling
/// `wait` is that the callers loop will degenerated to busy loop polling, pinning a CPU to
/// ~100% usage.
pub fn wait<'a>(&self, events: &'a EpollEvents) -> Result<PollEvents<'a, T>> {
self.wait_timeout(events, Duration::new(i64::MAX as u64, 0))
}
/// Like `wait` except will only block for a maximum of the given `timeout`.
///
/// This may return earlier than `timeout` with zero events if the duration indicated exceeds
/// system limits.
pub fn wait_timeout<'a>(
&self,
events: &'a EpollEvents,
timeout: Duration,
) -> Result<PollEvents<'a, T>> {
let timeout_millis = if timeout.as_secs() as i64 == i64::max_value() {
// We make the convenient assumption that 2^63 seconds is an effectively unbounded time
// frame. This is meant to mesh with `wait` calling us with no timeout.
-1
} else {
// In cases where we the number of milliseconds would overflow an i32, we substitute the
// maximum timeout which is ~24.8 days.
let millis = timeout
.as_secs()
.checked_mul(1_000)
.and_then(|ms| ms.checked_add(u64::from(timeout.subsec_nanos()) / 1_000_000))
.unwrap_or(i32::max_value() as u64);
min(i32::max_value() as u64, millis) as i32
};
let ret = {
let mut epoll_events = events.0.borrow_mut();
let max_events = epoll_events.len() as c_int;
// Safe because we give an epoll context and a properly sized epoll_events array
// pointer, which we trust the kernel to fill in properly.
unsafe {
handle_eintr_errno!(epoll_wait(
self.epoll_ctx.as_raw_fd(),
&mut epoll_events[0],
max_events,
timeout_millis
))
}
};
if ret < 0 {
return errno_result();
}
let epoll_events = events.0.borrow();
let events = PollEvents {
count: ret as usize,
events: epoll_events,
tokens: PhantomData,
};
Ok(events)
}
}
impl<T: PollToken> AsRawFd for EpollContext<T> {
fn as_raw_fd(&self) -> RawFd {
self.epoll_ctx.as_raw_fd()
}
}
impl<T: PollToken> IntoRawFd for EpollContext<T> {
fn into_raw_fd(self) -> RawFd {
self.epoll_ctx.into_raw_fd()
}
}
/// Used to poll multiple objects that have file descriptors.
///
/// # Example
///
/// ```
/// # use vmm_sys_util::{Result, EventFd, PollContext, PollEvents};
/// # fn test() -> Result<()> {
/// let evt1 = EventFd::new(0)?;
/// let evt2 = EventFd::new(0)?;
/// evt2.write(1)?;
///
/// let ctx: PollContext<u32> = PollContext::new()?;
/// ctx.add(&evt1, 1)?;
/// ctx.add(&evt2, 2)?;
///
/// let pollevents: PollEvents<u32> = ctx.wait()?;
/// let tokens: Vec<u32> = pollevents.iter_readable().map(|e| e.token()).collect();
/// assert_eq!(&tokens[..], &[2]);
/// # Ok(())
/// # }
/// ```
pub struct PollContext<T> {
epoll_ctx: EpollContext<T>,
// We use a RefCell here so that the `wait` method only requires an immutable self reference
// while returning the events (encapsulated by PollEvents). Without the RefCell, `wait` would
// hold a mutable reference that lives as long as its returned reference (i.e. the PollEvents),
// even though that reference is immutable. This is terribly inconvenient for the caller because
// the borrow checking would prevent them from using `delete` and `add` while the events are in
// scope.
events: EpollEvents,
// Hangup busy loop detection variables. See `check_for_hungup_busy_loop`.
hangups: Cell<usize>,
max_hangups: Cell<usize>,
}
impl<T: PollToken> PollContext<T> {
/// Creates a new `PollContext`.
pub fn new() -> Result<PollContext<T>> {
Ok(PollContext {
epoll_ctx: EpollContext::new()?,
events: EpollEvents::new(),
hangups: Cell::new(0),
max_hangups: Cell::new(0),
})
}
/// Adds the given `fd` to this context and associates the given `token` with the `fd`'s
/// readable events.
///
/// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
/// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
/// FD number) added to this context, events will not be reported by `wait` anymore.
pub fn add(&self, fd: &AsRawFd, token: T) -> Result<()> {
self.add_fd_with_events(fd, WatchingEvents::empty().set_read(), token)
}
/// Adds the given `fd` to this context, watching for the specified events and associates the
/// given 'token' with those events.
///
/// A `fd` can only be added once and does not need to be kept open. If the `fd` is dropped and
/// there were no duplicated file descriptors (i.e. adding the same descriptor with a different
/// FD number) added to this context, events will not be reported by `wait` anymore.
pub fn add_fd_with_events(&self, fd: &AsRawFd, events: WatchingEvents, token: T) -> Result<()> {
self.epoll_ctx.add_fd_with_events(fd, events, token)?;
self.hangups.set(0);
self.max_hangups.set(self.max_hangups.get() + 1);
Ok(())
}
/// If `fd` was previously added to this context, the watched events will be replaced with
/// `events` and the token associated with it will be replaced with the given `token`.
pub fn modify(&self, fd: &AsRawFd, events: WatchingEvents, token: T) -> Result<()> {
self.epoll_ctx.modify(fd, events, token)
}
/// Deletes the given `fd` from this context.
///
/// If an `fd`'s token shows up in the list of hangup events, it should be removed using this
/// method or by closing/dropping (if and only if the fd was never dup()'d/fork()'d) the `fd`.
/// Failure to do so will cause the `wait` method to always return immediately, causing ~100%
/// CPU load.
pub fn delete(&self, fd: &AsRawFd) -> Result<()> {
self.epoll_ctx.delete(fd)?;
self.hangups.set(0);
self.max_hangups.set(self.max_hangups.get() - 1);
Ok(())
}
// This method determines if the the user of wait is misusing the `PollContext` by leaving FDs
// in this `PollContext` that have been shutdown or hungup on. Such an FD will cause `wait` to
// return instantly with a hungup event. If that FD is perpetually left in this context, a busy
// loop burning ~100% of one CPU will silently occur with no human visible malfunction.
//
// How do we know if the client of this context is ignoring hangups? A naive implementation
// would trigger if consecutive wait calls yield hangup events, but there are legitimate cases
// for this, such as two distinct sockets becoming hungup across two consecutive wait calls. A
// smarter implementation would only trigger if `delete` wasn't called between waits that
// yielded hangups. Sadly `delete` isn't the only way to remove an FD from this context. The
// other way is for the client to close the hungup FD, which automatically removes it from this
// context. Assuming that the client always uses close, this implementation would too eagerly
// trigger.
//
// The implementation used here keeps an upper bound of FDs in this context using a counter
// hooked into add/delete (which is imprecise because close can also remove FDs without us
// knowing). The number of consecutive (no add or delete in between) hangups yielded by wait
// calls is counted and compared to the upper bound. If the upper bound is exceeded by the
// consecutive hangups, the implementation triggers the check and logs.
//
// This implementation has false negatives because the upper bound can be completely too high,
// in the worst case caused by only using close instead of delete. However, this method has the
// advantage of always triggering eventually genuine busy loop cases, requires no dynamic
// allocations, is fast and constant time to compute, and has no false positives.
fn check_for_hungup_busy_loop(&self, new_hangups: usize) {
let old_hangups = self.hangups.get();
let max_hangups = self.max_hangups.get();
if old_hangups <= max_hangups && old_hangups + new_hangups > max_hangups {
warn!(
"busy poll wait loop with hungup FDs detected on thread {}",
thread::current().name().unwrap_or("")
);
// This panic is helpful for tests of this functionality.
#[cfg(test)]
panic!("hungup busy loop detected");
}
self.hangups.set(old_hangups + new_hangups);
}
/// Waits for any events to occur in FDs that were previously added to this context.
///
/// The events are level-triggered, meaning that if any events are unhandled (i.e. not reading
/// for readable events and not closing for hungup events), subsequent calls to `wait` will
/// return immediately. The consequence of not handling an event perpetually while calling
/// `wait` is that the callers loop will degenerated to busy loop polling, pinning a CPU to
/// ~100% usage.
///
/// # Panics
/// Panics if the returned `PollEvents` structure is not dropped before subsequent `wait` calls.
pub fn wait(&self) -> Result<PollEvents<T>> {
self.wait_timeout(Duration::new(i64::MAX as u64, 0))
}
/// Like `wait` except will only block for a maximum of the given `timeout`.
///
/// This may return earlier than `timeout` with zero events if the duration indicated exceeds
/// system limits.
pub fn wait_timeout(&self, timeout: Duration) -> Result<PollEvents<T>> {
let events = self.epoll_ctx.wait_timeout(&self.events, timeout)?;
let hangups = events.iter_hungup().count();
self.check_for_hungup_busy_loop(hangups);
Ok(events)
}
}
impl<T: PollToken> AsRawFd for PollContext<T> {
fn as_raw_fd(&self) -> RawFd {
self.epoll_ctx.as_raw_fd()
}
}
impl<T: PollToken> IntoRawFd for PollContext<T> {
fn into_raw_fd(self) -> RawFd {
self.epoll_ctx.into_raw_fd()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::eventfd::EventFd;
use std::os::unix::net::UnixStream;
use std::time::Instant;
#[test]
fn test_poll_context() {
let evt1 = EventFd::new(0).unwrap();
let evt2 = EventFd::new(0).unwrap();
evt1.write(1).unwrap();
evt2.write(1).unwrap();
let ctx: PollContext<u32> = PollContext::new().unwrap();
ctx.add(&evt1, 1).unwrap();
ctx.add(&evt2, 2).unwrap();
let mut evt_count = 0;
while evt_count < 2 {
for event in ctx.wait().unwrap().iter_readable() {
evt_count += 1;
match event.token() {
1 => {
evt1.read().unwrap();
ctx.delete(&evt1).unwrap();
}
2 => {
evt2.read().unwrap();
ctx.delete(&evt2).unwrap();
}
_ => panic!("unexpected token"),
};
}
}
assert_eq!(evt_count, 2);
}
#[test]
fn test_poll_context_overflow() {
const EVT_COUNT: usize = POLL_CONTEXT_MAX_EVENTS * 2 + 1;
let ctx: PollContext<usize> = PollContext::new().unwrap();
let mut evts = Vec::with_capacity(EVT_COUNT);
for i in 0..EVT_COUNT {
let evt = EventFd::new(0).unwrap();
evt.write(1).unwrap();
ctx.add(&evt, i).unwrap();
evts.push(evt);
}
let mut evt_count = 0;
while evt_count < EVT_COUNT {
for event in ctx.wait().unwrap().iter_readable() {
evts[event.token()].read().unwrap();
evt_count += 1;
}
}
}
#[test]
#[should_panic]
fn test_poll_context_hungup() {
let (s1, s2) = UnixStream::pair().unwrap();
let ctx: PollContext<u32> = PollContext::new().unwrap();
ctx.add(&s1, 1).unwrap();
// Causes s1 to receive hangup events, which we purposefully ignore to trip the detection
// logic in `PollContext`.
drop(s2);
// Should easily panic within this many iterations.
for _ in 0..1000 {
ctx.wait().unwrap();
}
}
#[test]
fn test_poll_context_timeout() {
let ctx: PollContext<u32> = PollContext::new().unwrap();
let dur = Duration::from_millis(10);
let start_inst = Instant::now();
ctx.wait_timeout(dur).unwrap();
assert!(start_inst.elapsed() >= dur);
}
}

View File

@ -1,215 +0,0 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: BSD-3-Clause
use std::fs::File;
use std::io::{Error, Result};
use std::os::unix::io::AsRawFd;
#[cfg(target_env = "musl")]
use libc::{c_int, lseek64, ENXIO};
#[cfg(target_env = "gnu")]
use libc::{lseek64, ENXIO, SEEK_DATA, SEEK_HOLE};
/// A trait for seeking to the next hole or non-hole position in a file.
pub trait SeekHole {
/// Seek to the first hole in a file at a position greater than or equal to `offset`.
/// If no holes exist after `offset`, the seek position will be set to the end of the file.
/// If `offset` is at or after the end of the file, the seek position is unchanged, and None is returned.
/// Returns the current seek position after the seek or an error.
fn seek_hole(&mut self, offset: u64) -> Result<Option<u64>>;
/// Seek to the first data in a file at a position greater than or equal to `offset`.
/// If no data exists after `offset`, the seek position is unchanged, and None is returned.
/// Returns the current offset after the seek or an error.
fn seek_data(&mut self, offset: u64) -> Result<Option<u64>>;
}
#[cfg(target_env = "musl")]
pub const SEEK_DATA: c_int = 3;
#[cfg(target_env = "musl")]
pub const SEEK_HOLE: c_int = 4;
/// Safe wrapper for `libc::lseek64()`
fn lseek(file: &mut File, offset: i64, whence: i32) -> Result<Option<u64>> {
// This is safe because we pass a known-good file descriptor.
let res = unsafe { lseek64(file.as_raw_fd(), offset, whence) };
if res < 0 {
// Convert ENXIO into None; pass any other error as-is.
let err = Error::last_os_error();
if let Some(errno) = Error::raw_os_error(&err) {
if errno == ENXIO {
return Ok(None);
}
}
Err(err)
} else {
Ok(Some(res as u64))
}
}
impl SeekHole for File {
fn seek_hole(&mut self, offset: u64) -> Result<Option<u64>> {
lseek(self, offset as i64, SEEK_HOLE)
}
fn seek_data(&mut self, offset: u64) -> Result<Option<u64>> {
lseek(self, offset as i64, SEEK_DATA)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::TempDir;
use std::fs::File;
use std::io::{Seek, SeekFrom, Write};
use std::path::PathBuf;
fn seek_cur(file: &mut File) -> u64 {
file.seek(SeekFrom::Current(0)).unwrap()
}
#[test]
fn seek_data() {
let tempdir = TempDir::new("/tmp/seek_data_test").unwrap();
let mut path = PathBuf::from(tempdir.as_path().unwrap());
path.push("test_file");
let mut file = File::create(&path).unwrap();
// Empty file
assert_eq!(file.seek_data(0).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
// File with non-zero length consisting entirely of a hole
file.set_len(0x10000).unwrap();
assert_eq!(file.seek_data(0).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
// seek_data at or after the end of the file should return None
assert_eq!(file.seek_data(0x10000).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
assert_eq!(file.seek_data(0x10001).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
// Write some data to [0x10000, 0x20000)
let b = [0x55u8; 0x10000];
file.seek(SeekFrom::Start(0x10000)).unwrap();
file.write_all(&b).unwrap();
assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
assert_eq!(seek_cur(&mut file), 0x10000);
// seek_data within data should return the same offset
assert_eq!(file.seek_data(0x10000).unwrap(), Some(0x10000));
assert_eq!(seek_cur(&mut file), 0x10000);
assert_eq!(file.seek_data(0x10001).unwrap(), Some(0x10001));
assert_eq!(seek_cur(&mut file), 0x10001);
assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
assert_eq!(seek_cur(&mut file), 0x1FFFF);
// Extend the file to add another hole after the data
file.set_len(0x30000).unwrap();
assert_eq!(file.seek_data(0).unwrap(), Some(0x10000));
assert_eq!(seek_cur(&mut file), 0x10000);
assert_eq!(file.seek_data(0x1FFFF).unwrap(), Some(0x1FFFF));
assert_eq!(seek_cur(&mut file), 0x1FFFF);
assert_eq!(file.seek_data(0x20000).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0x1FFFF);
}
#[test]
#[allow(clippy::cyclomatic_complexity)]
fn seek_hole() {
let tempdir = TempDir::new("/tmp/seek_hole_test").unwrap();
let mut path = PathBuf::from(tempdir.as_path().unwrap());
path.push("test_file");
let mut file = File::create(&path).unwrap();
// Empty file
assert_eq!(file.seek_hole(0).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
// File with non-zero length consisting entirely of a hole
file.set_len(0x10000).unwrap();
assert_eq!(file.seek_hole(0).unwrap(), Some(0));
assert_eq!(seek_cur(&mut file), 0);
assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
assert_eq!(seek_cur(&mut file), 0xFFFF);
// seek_hole at or after the end of the file should return None
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x10000).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
assert_eq!(file.seek_hole(0x10001).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
// Write some data to [0x10000, 0x20000)
let b = [0x55u8; 0x10000];
file.seek(SeekFrom::Start(0x10000)).unwrap();
file.write_all(&b).unwrap();
// seek_hole within a hole should return the same offset
assert_eq!(file.seek_hole(0).unwrap(), Some(0));
assert_eq!(seek_cur(&mut file), 0);
assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
assert_eq!(seek_cur(&mut file), 0xFFFF);
// seek_hole within data should return the next hole (EOF)
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
assert_eq!(seek_cur(&mut file), 0x20000);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x10001).unwrap(), Some(0x20000));
assert_eq!(seek_cur(&mut file), 0x20000);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
assert_eq!(seek_cur(&mut file), 0x20000);
// seek_hole at EOF after data should return None
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x20000).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
// Extend the file to add another hole after the data
file.set_len(0x30000).unwrap();
assert_eq!(file.seek_hole(0).unwrap(), Some(0));
assert_eq!(seek_cur(&mut file), 0);
assert_eq!(file.seek_hole(0xFFFF).unwrap(), Some(0xFFFF));
assert_eq!(seek_cur(&mut file), 0xFFFF);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x10000).unwrap(), Some(0x20000));
assert_eq!(seek_cur(&mut file), 0x20000);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x1FFFF).unwrap(), Some(0x20000));
assert_eq!(seek_cur(&mut file), 0x20000);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x20000));
assert_eq!(seek_cur(&mut file), 0x20000);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x20001));
assert_eq!(seek_cur(&mut file), 0x20001);
// seek_hole at EOF after a hole should return None
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x30000).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
// Write some data to [0x20000, 0x30000)
file.seek(SeekFrom::Start(0x20000)).unwrap();
file.write_all(&b).unwrap();
// seek_hole within [0x20000, 0x30000) should now find the hole at EOF
assert_eq!(file.seek_hole(0x20000).unwrap(), Some(0x30000));
assert_eq!(seek_cur(&mut file), 0x30000);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x20001).unwrap(), Some(0x30000));
assert_eq!(seek_cur(&mut file), 0x30000);
file.seek(SeekFrom::Start(0)).unwrap();
assert_eq!(file.seek_hole(0x30000).unwrap(), None);
assert_eq!(seek_cur(&mut file), 0);
}
}

View File

@ -1,420 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Portions Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use libc::{
c_int, c_void, pthread_kill, pthread_sigmask, pthread_t, sigaction, sigaddset, sigemptyset,
siginfo_t, sigismember, sigpending, sigset_t, sigtimedwait, timespec, EAGAIN, EINTR, EINVAL,
SIGHUP, SIGSYS, SIG_BLOCK, SIG_UNBLOCK,
};
use errno;
use std::fmt::{self, Display};
use std::io;
use std::mem;
use std::os::unix::thread::JoinHandleExt;
use std::ptr::{null, null_mut};
use std::result;
use std::thread::JoinHandle;
#[derive(Debug)]
pub enum Error {
/// Couldn't create a sigset.
CreateSigset(errno::Error),
/// The wrapped signal has already been blocked.
SignalAlreadyBlocked(c_int),
/// Failed to check if the requested signal is in the blocked set already.
CompareBlockedSignals(errno::Error),
/// The signal could not be blocked.
BlockSignal(errno::Error),
/// The signal mask could not be retrieved.
RetrieveSignalMask(i32),
/// The signal could not be unblocked.
UnblockSignal(errno::Error),
/// Failed to wait for given signal.
ClearWaitPending(errno::Error),
/// Failed to get pending signals.
ClearGetPending(errno::Error),
/// Failed to check if given signal is in the set of pending signals.
ClearCheckPending(errno::Error),
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
CreateSigset(e) => write!(f, "couldn't create a sigset: {}", e),
SignalAlreadyBlocked(num) => write!(f, "signal {} already blocked", num),
CompareBlockedSignals(e) => write!(
f,
"failed to check whether requested signal is in the blocked set: {}",
e,
),
BlockSignal(e) => write!(f, "signal could not be blocked: {}", e),
RetrieveSignalMask(errno) => write!(
f,
"failed to retrieve signal mask: {}",
io::Error::from_raw_os_error(*errno),
),
UnblockSignal(e) => write!(f, "signal could not be unblocked: {}", e),
ClearWaitPending(e) => write!(f, "failed to wait for given signal: {}", e),
ClearGetPending(e) => write!(f, "failed to get pending signals: {}", e),
ClearCheckPending(e) => write!(
f,
"failed to check whether given signal is in the pending set: {}",
e,
),
}
}
}
pub type SignalResult<T> = result::Result<T, Error>;
type SiginfoHandler = extern "C" fn(num: c_int, info: *mut siginfo_t, _unused: *mut c_void) -> ();
pub enum SignalHandler {
Siginfo(SiginfoHandler),
// TODO add a`SimpleHandler` when `libc` adds `sa_handler` support to `sigaction`.
}
impl SignalHandler {
fn set_flags(act: &mut sigaction, flag: c_int) {
act.sa_flags = flag;
}
}
/// Fills a `sigaction` structure from of the signal handler.
/// Refer to http://man7.org/linux/man-pages/man7/signal.7.html
impl Into<sigaction> for SignalHandler {
fn into(self) -> sigaction {
let mut act: sigaction = unsafe { mem::zeroed() };
match self {
SignalHandler::Siginfo(function) => {
act.sa_sigaction = function as *const () as usize;
}
}
act
}
}
extern "C" {
fn __libc_current_sigrtmin() -> c_int;
fn __libc_current_sigrtmax() -> c_int;
}
/// Returns the minimum (inclusive) real-time signal number.
#[allow(non_snake_case)]
fn SIGRTMIN() -> c_int {
unsafe { __libc_current_sigrtmin() }
}
/// Returns the maximum (inclusive) real-time signal number.
#[allow(non_snake_case)]
fn SIGRTMAX() -> c_int {
unsafe { __libc_current_sigrtmax() }
}
/// Verifies that a signal number is valid: for VCPU signals, it needs to be enclosed within the OS
/// limits for realtime signals, and the remaining ones need to be between the minimum (SIGHUP) and
/// maximum (SIGSYS) values.
pub fn validate_signal_num(num: c_int, for_vcpu: bool) -> errno::Result<c_int> {
if for_vcpu {
let actual_num = num + SIGRTMIN();
if actual_num <= SIGRTMAX() {
return Ok(actual_num);
}
} else if SIGHUP <= num && num <= SIGSYS {
return Ok(num);
}
Err(errno::Error::new(EINVAL))
}
/// Registers `handler` as the signal handler of signum `num`.
///
/// Uses `sigaction` to register the handler.
///
/// This is considered unsafe because the given handler will be called asynchronously, interrupting
/// whatever the thread was doing and therefore must only do async-signal-safe operations.
/// flags: SA_SIGINFO or SA_RESTART if wants to restart after signal received.
pub unsafe fn register_signal_handler(
num: i32,
handler: SignalHandler,
for_vcpu: bool,
flag: c_int,
) -> errno::Result<()> {
let num = validate_signal_num(num, for_vcpu)?;
let mut act: sigaction = handler.into();
SignalHandler::set_flags(&mut act, flag);
match sigaction(num, &act, null_mut()) {
0 => Ok(()),
_ => errno::errno_result(),
}
}
/// Creates `sigset` from an array of signal numbers.
///
/// This is a helper function used when we want to manipulate signals.
pub fn create_sigset(signals: &[c_int]) -> errno::Result<sigset_t> {
// sigset will actually be initialized by sigemptyset below.
let mut sigset: sigset_t = unsafe { mem::zeroed() };
// Safe - return value is checked.
let ret = unsafe { sigemptyset(&mut sigset) };
if ret < 0 {
return errno::errno_result();
}
for signal in signals {
// Safe - return value is checked.
let ret = unsafe { sigaddset(&mut sigset, *signal) };
if ret < 0 {
return errno::errno_result();
}
}
Ok(sigset)
}
/// Retrieves the signal mask of the current thread as a vector of c_ints.
pub fn get_blocked_signals() -> SignalResult<Vec<c_int>> {
let mut mask = Vec::new();
// Safe - return values are checked.
unsafe {
let mut old_sigset: sigset_t = mem::zeroed();
let ret = pthread_sigmask(SIG_BLOCK, null(), &mut old_sigset as *mut sigset_t);
if ret < 0 {
return Err(Error::RetrieveSignalMask(ret));
}
for num in 0..=SIGRTMAX() {
if sigismember(&old_sigset, num) > 0 {
mask.push(num);
}
}
}
Ok(mask)
}
/// Masks given signal.
///
/// If signal is already blocked the call will fail with Error::SignalAlreadyBlocked
/// result.
pub fn block_signal(num: c_int) -> SignalResult<()> {
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
// Safe - return values are checked.
unsafe {
let mut old_sigset: sigset_t = mem::zeroed();
let ret = pthread_sigmask(SIG_BLOCK, &sigset, &mut old_sigset as *mut sigset_t);
if ret < 0 {
return Err(Error::BlockSignal(errno::Error::last()));
}
let ret = sigismember(&old_sigset, num);
if ret < 0 {
return Err(Error::CompareBlockedSignals(errno::Error::last()));
} else if ret > 0 {
return Err(Error::SignalAlreadyBlocked(num));
}
}
Ok(())
}
/// Unmasks given signal.
pub fn unblock_signal(num: c_int) -> SignalResult<()> {
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
// Safe - return value is checked.
let ret = unsafe { pthread_sigmask(SIG_UNBLOCK, &sigset, null_mut()) };
if ret < 0 {
return Err(Error::UnblockSignal(errno::Error::last()));
}
Ok(())
}
/// Clears pending signal.
pub fn clear_signal(num: c_int) -> SignalResult<()> {
let sigset = create_sigset(&[num]).map_err(Error::CreateSigset)?;
while {
// This is safe as we are rigorously checking return values
// of libc calls.
unsafe {
let mut siginfo: siginfo_t = mem::zeroed();
let ts = timespec {
tv_sec: 0,
tv_nsec: 0,
};
// Attempt to consume one instance of pending signal. If signal
// is not pending, the call will fail with EAGAIN or EINTR.
let ret = sigtimedwait(&sigset, &mut siginfo, &ts);
if ret < 0 {
let e = errno::Error::last();
match e.errno() {
EAGAIN | EINTR => {}
_ => {
return Err(Error::ClearWaitPending(errno::Error::last()));
}
}
}
// This sigset will be actually filled with `sigpending` call.
let mut chkset: sigset_t = mem::zeroed();
// See if more instances of the signal are pending.
let ret = sigpending(&mut chkset);
if ret < 0 {
return Err(Error::ClearGetPending(errno::Error::last()));
}
let ret = sigismember(&chkset, num);
if ret < 0 {
return Err(Error::ClearCheckPending(errno::Error::last()));
}
// This is do-while loop condition.
ret != 0
}
} {}
Ok(())
}
/// Trait for threads that can be signalled via `pthread_kill`.
///
/// Note that this is only useful for signals between SIGRTMIN and SIGRTMAX because these are
/// guaranteed to not be used by the C runtime.
///
/// This is marked unsafe because the implementation of this trait must guarantee that the returned
/// pthread_t is valid and has a lifetime at least that of the trait object.
pub unsafe trait Killable {
fn pthread_handle(&self) -> pthread_t;
/// Sends the signal `num + SIGRTMIN` to this killable thread.
///
/// The value of `num + SIGRTMIN` must not exceed `SIGRTMAX`.
fn kill(&self, num: i32) -> errno::Result<()> {
let num = validate_signal_num(num, true)?;
// Safe because we ensure we are using a valid pthread handle, a valid signal number, and
// check the return result.
let ret = unsafe { pthread_kill(self.pthread_handle(), num) };
if ret < 0 {
return errno::errno_result();
}
Ok(())
}
}
// Safe because we fulfill our contract of returning a genuine pthread handle.
unsafe impl<T> Killable for JoinHandle<T> {
fn pthread_handle(&self) -> pthread_t {
self.as_pthread_t()
}
}
#[cfg(test)]
mod tests {
use super::*;
use libc::SA_SIGINFO;
use std::thread;
use std::time::Duration;
static mut SIGNAL_HANDLER_CALLED: bool = false;
extern "C" fn handle_signal(_: c_int, _: *mut siginfo_t, _: *mut c_void) {
unsafe {
SIGNAL_HANDLER_CALLED = true;
}
}
#[test]
fn test_register_signal_handler() {
unsafe {
// testing bad value
assert!(register_signal_handler(
SIGRTMAX(),
SignalHandler::Siginfo(handle_signal),
true,
SA_SIGINFO
)
.is_err());
format!(
"{:?}",
register_signal_handler(
SIGRTMAX(),
SignalHandler::Siginfo(handle_signal),
true,
SA_SIGINFO
)
);
assert!(register_signal_handler(
0,
SignalHandler::Siginfo(handle_signal),
true,
SA_SIGINFO
)
.is_ok());
assert!(register_signal_handler(
libc::SIGSYS,
SignalHandler::Siginfo(handle_signal),
false,
SA_SIGINFO
)
.is_ok());
}
}
#[test]
#[allow(clippy::empty_loop)]
fn test_killing_thread() {
let killable = thread::spawn(|| thread::current().id());
let killable_id = killable.join().unwrap();
assert_ne!(killable_id, thread::current().id());
// We install a signal handler for the specified signal; otherwise the whole process will
// be brought down when the signal is received, as part of the default behaviour. Signal
// handlers are global, so we install this before starting the thread.
unsafe {
register_signal_handler(0, SignalHandler::Siginfo(handle_signal), true, SA_SIGINFO)
.expect("failed to register vcpu signal handler");
}
let killable = thread::spawn(|| loop {});
let res = killable.kill(SIGRTMAX());
assert!(res.is_err());
format!("{:?}", res);
unsafe {
assert!(!SIGNAL_HANDLER_CALLED);
}
assert!(killable.kill(0).is_ok());
// We're waiting to detect that the signal handler has been called.
const MAX_WAIT_ITERS: u32 = 20;
let mut iter_count = 0;
loop {
thread::sleep(Duration::from_millis(100));
if unsafe { SIGNAL_HANDLER_CALLED } {
break;
}
iter_count += 1;
// timeout if we wait too long
assert!(iter_count <= MAX_WAIT_ITERS);
}
// Our signal handler doesn't do anything which influences the killable thread, so the
// previous signal is effectively ignored. If we were to join killable here, we would block
// forever as the loop keeps running. Since we don't join, the thread will become detached
// as the handle is dropped, and will be killed when the process/main thread exits.
}
}

View File

@ -1,643 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//! Facilities for sending log message to syslog.
//!
//! Every function exported by this module is thread-safe. Each function will silently fail until
//! `syslog::init()` is called and returns `Ok`.
//!
//! # Examples
//!
//! ```
//! #[macro_use]
//! extern crate vmm_sys_util;
//!
//! use vmm_sys_util::syslog::init;
//! fn main() {
//! if let Err(e) = init() {
//! println!("failed to initiailize syslog: {}", e);
//! return;
//! }
//! warn!("this is your {} warning", "final");
//! error!("something went horribly wrong: {}", "out of RAMs");
//! }
//! ```
use std::env;
use std::ffi::CString;
use std::ffi::{OsStr, OsString};
use std::fmt::{self, Display};
use std::fs::File;
use std::io;
use std::io::{stderr, Cursor, ErrorKind, Write};
use std::mem;
use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
use std::os::unix::net::UnixDatagram;
use std::path::PathBuf;
use std::ptr::null;
use std::str::from_utf8;
use std::sync::{Mutex as StdMutex, MutexGuard, Once, ONCE_INIT};
use libc::{
c_char, c_long, closelog, fcntl, gethostname, localtime_r, openlog, pid_t, syscall, time,
time_t, tm, F_GETFD, LOG_NDELAY, LOG_PERROR, LOG_PID, LOG_USER,
};
/// Temporary define linux-x86_64 syscall value here.
#[allow(non_upper_case_globals)]
pub const SYS_getpid: c_long = 39;
const SYSLOG_PATH: &str = "/dev/log";
/// The priority (i.e. severity) of a syslog message.
///
/// See syslog man pages for information on their semantics.
#[derive(Copy, Clone, Debug)]
pub enum Priority {
Emergency = 0,
Alert = 1,
Critical = 2,
Error = 3,
Warning = 4,
Notice = 5,
Info = 6,
Debug = 7,
}
impl fmt::Display for Priority {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
Priority::Emergency => write!(f, "EMERGENCY"),
Priority::Alert => write!(f, "ALERT"),
Priority::Critical => write!(f, "CRITICAL"),
Priority::Error => write!(f, "ERROR"),
Priority::Warning => write!(f, "WARNING"),
Priority::Notice => write!(f, "NOTICE"),
Priority::Info => write!(f, "INFO"),
Priority::Debug => write!(f, "DEBUG"),
}
}
}
/// The facility of a syslog message.
///
/// See syslog man pages for information on their semantics.
pub enum Facility {
Kernel = 0,
User = 1 << 3,
Mail = 2 << 3,
Daemon = 3 << 3,
Auth = 4 << 3,
Syslog = 5 << 3,
Lpr = 6 << 3,
News = 7 << 3,
Uucp = 8 << 3,
Local0 = 16 << 3,
Local1 = 17 << 3,
Local2 = 18 << 3,
Local3 = 19 << 3,
Local4 = 20 << 3,
Local5 = 21 << 3,
Local6 = 22 << 3,
Local7 = 23 << 3,
}
/// Errors returned by `syslog::init()`.
#[derive(Debug)]
pub enum Error {
/// Initialization was never attempted.
NeverInitialized,
/// Initialization has previously failed and can not be retried.
Poisoned,
/// Error while creating socket.
Socket(io::Error),
/// Error while attempting to connect socket.
Connect(io::Error),
// There was an error using `open` to get the lowest file descriptor.
GetLowestFd(io::Error),
// The guess of libc's file descriptor for the syslog connection was invalid.
InvalidFd,
}
impl Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use self::Error::*;
match self {
NeverInitialized => write!(f, "initialization was never attempted"),
Poisoned => write!(f, "initialization previously failed and cannot be retried"),
Socket(e) => write!(f, "failed to create socket: {}", e),
Connect(e) => write!(f, "failed to connect socket: {}", e),
GetLowestFd(e) => write!(f, "failed to get lowest file descriptor: {}", e),
InvalidFd => write!(f, "guess of fd for syslog connection was invalid"),
}
}
}
fn get_hostname() -> Result<String, ()> {
let mut hostname: [u8; 256] = [b'\0'; 256];
// Safe because we give a valid pointer to a buffer of the indicated length and check for the
// result.
let ret = unsafe { gethostname(hostname.as_mut_ptr() as *mut c_char, hostname.len()) };
if ret == -1 {
return Err(());
}
let len = hostname.iter().position(|&v| v == b'\0').ok_or(())?;
Ok(from_utf8(&hostname[..len]).map_err(|_| ())?.to_string())
}
fn get_proc_name() -> Option<String> {
env::args_os()
.next()
.map(PathBuf::from)
.and_then(|s| s.file_name().map(OsStr::to_os_string))
.map(OsString::into_string)
.and_then(Result::ok)
}
// Uses libc's openlog function to get a socket to the syslogger. By getting the socket this way, as
// opposed to connecting to the syslogger directly, libc's internal state gets initialized for other
// libraries (e.g. minijail) that make use of libc's syslog function. Note that this function
// depends on no other threads or signal handlers being active in this process because they might
// create FDs.
//
// TODO(zachr): Once https://android-review.googlesource.com/470998 lands, there won't be any
// libraries in use that hard depend on libc's syslogger. Remove this and go back to making the
// connection directly once minjail is ready.
fn openlog_and_get_socket() -> Result<UnixDatagram, Error> {
// closelog first in case there was already a file descriptor open. Safe because it takes no
// arguments and just closes an open file descriptor. Does nothing if the file descriptor
// was not already open.
unsafe {
closelog();
}
let file_path = CString::new("/dev/null").unwrap();
unsafe {
// Ordinarily libc's FD for the syslog connection can't be accessed, but we can guess that the
// FD that openlog will be getting is the lowest unused FD. To guarantee that an FD is opened in
// this function we use the LOG_NDELAY to tell openlog to connect to the syslog now. To get the
// lowest unused FD, we open a dummy file (which the manual says will always return the lowest
// fd), and then close that fd. Voilà, we now know the lowest numbered FD. The call to openlog
// will make use of that FD, and then we just wrap a `UnixDatagram` around it for ease of use.
let fd = libc::open(file_path.as_ptr(), libc::O_RDONLY);
if fd < 0 {
let err = io::Error::last_os_error();
return Err(Error::GetLowestFd(err));
}
// Safe because openlog accesses no pointers because `ident` is null, only valid flags are
// used, and it returns no error.
openlog(null(), LOG_NDELAY | LOG_PERROR | LOG_PID, LOG_USER);
// For safety, ensure the fd we guessed is valid. The `fcntl` call itself only reads the
// file descriptor table of the current process, which is trivially safe.
if fcntl(fd, F_GETFD) >= 0 {
Ok(UnixDatagram::from_raw_fd(fd))
} else {
Err(Error::InvalidFd)
}
}
}
struct State {
stderr: bool,
socket: Option<UnixDatagram>,
file: Option<File>,
hostname: Option<String>,
proc_name: Option<String>,
}
impl State {
fn new() -> Result<State, Error> {
let s = openlog_and_get_socket()?;
Ok(State {
stderr: true,
socket: Some(s),
file: None,
hostname: get_hostname().ok(),
proc_name: get_proc_name(),
})
}
}
static STATE_ONCE: Once = ONCE_INIT;
static mut STATE: *const StdMutex<State> = 0 as *const _;
fn new_mutex_ptr<T>(inner: T) -> *const StdMutex<T> {
Box::into_raw(Box::new(StdMutex::new(inner)))
}
/// Initialize the syslog connection and internal variables.
///
/// This should only be called once per process before any other threads have been spawned or any
/// signal handlers have been registered. Every call made after the first will have no effect
/// besides return `Ok` or `Err` appropriately.
pub fn init() -> Result<(), Error> {
let mut err = Error::Poisoned;
STATE_ONCE.call_once(|| match State::new() {
// Safe because STATE mutation is guarded by `Once`.
Ok(state) => unsafe { STATE = new_mutex_ptr(state) },
Err(e) => err = e,
});
if unsafe { STATE.is_null() } {
Err(err)
} else {
Ok(())
}
}
fn lock() -> Result<MutexGuard<'static, State>, Error> {
// Safe because we assume that STATE is always in either a valid or NULL state.
let state_ptr = unsafe { STATE };
if state_ptr.is_null() {
return Err(Error::NeverInitialized);
}
// Safe because STATE only mutates once and we checked for NULL.
let state = unsafe { &*state_ptr };
let guard = match state.lock() {
Ok(guard) => guard,
_ => panic!("mutex is poisoned"),
};
Ok(guard)
}
// Attempts to lock and retrieve the state. Returns from the function silently on failure.
macro_rules! lock {
() => {
match lock() {
Ok(s) => s,
_ => return,
};
};
}
/// Replaces the hostname reported in each syslog message.
///
/// The default hostname is whatever `gethostname()` returned when `vmm_sys_util::syslog::init()` was first
/// called.
///
/// Does nothing if syslog was never initialized.
pub fn set_hostname<T: Into<String>>(hostname: T) {
let mut state = lock!();
state.hostname = Some(hostname.into());
}
/// Replaces the process name reported in each syslog message.
///
/// The default process name is the _file name_ of `argv[0]`. For example, if this program was
/// invoked as
///
/// ```bash
/// $ path/to/app --delete everything
/// ```
///
/// the default process name would be _app_.
///
/// Does nothing if syslog was never initialized.
pub fn set_proc_name<T: Into<String>>(proc_name: T) {
let mut state = lock!();
state.proc_name = Some(proc_name.into());
}
/// Enables or disables echoing log messages to the syslog.
///
/// The default behavior is **enabled**.
///
/// If `enable` goes from `true` to `false`, the syslog connection is closed. The connection is
/// reopened if `enable` is set to `true` after it became `false`.
///
/// Returns an error if syslog was never initialized or the syslog connection failed to be
/// established.
///
/// # Arguments
/// * `enable` - `true` to enable echoing to syslog, `false` to disable echoing to syslog.
pub fn echo_syslog(enable: bool) -> Result<(), Error> {
let state_ptr = unsafe { STATE };
if state_ptr.is_null() {
return Err(Error::NeverInitialized);
}
let mut state = lock().map_err(|_| Error::Poisoned)?;
match state.socket.take() {
Some(_) if enable => {}
Some(s) => {
// Because `openlog_and_get_socket` actually just "borrows" the syslog FD, this module
// does not own the syslog connection and therefore should not destroy it.
mem::forget(s);
}
None if enable => {
let s = openlog_and_get_socket()?;
state.socket = Some(s);
}
_ => {}
}
Ok(())
}
/// Replaces the optional `File` to echo log messages to.
///
/// The default behavior is to not echo to a file. Passing `None` to this function restores that
/// behavior.
///
/// Does nothing if syslog was never initialized.
///
/// # Arguments
/// * `file` - `Some(file)` to echo to `file`, `None` to disable echoing to the file previously passed to `echo_file`.
pub fn echo_file(file: Option<File>) {
let mut state = lock!();
state.file = file;
}
/// Enables or disables echoing log messages to the `std::io::stderr()`.
///
/// The default behavior is **enabled**.
///
/// Does nothing if syslog was never initialized.
///
/// # Arguments
/// * `enable` - `true` to enable echoing to stderr, `false` to disable echoing to stderr.
pub fn echo_stderr(enable: bool) {
let mut state = lock!();
state.stderr = enable;
}
/// Retrieves the file descriptors owned by the global syslogger.
///
/// Does nothing if syslog was never initialized. If their are any file descriptors, they will be
/// pushed into `fds`.
///
/// Note that the `stderr` file descriptor is never added, as it is not owned by syslog.
#[allow(clippy::redundant_closure)]
pub fn push_fds(fds: &mut Vec<RawFd>) {
let state = lock!();
fds.extend(state.socket.iter().map(|s| s.as_raw_fd()));
fds.extend(state.file.iter().map(|f| f.as_raw_fd()));
}
/// Should only be called after `init()` was called.
fn send_buf(socket: &UnixDatagram, buf: &[u8]) {
const SEND_RETRY: usize = 2;
for _ in 0..SEND_RETRY {
match socket.send(&buf[..]) {
Ok(_) => break,
Err(e) => match e.kind() {
ErrorKind::ConnectionRefused
| ErrorKind::ConnectionReset
| ErrorKind::ConnectionAborted
| ErrorKind::NotConnected => {
let res = socket.connect(SYSLOG_PATH);
if res.is_err() {
break;
}
}
_ => {}
},
}
}
}
fn get_localtime() -> tm {
unsafe {
// Safe because tm is just a struct of plain data.
let mut tm: tm = mem::zeroed();
let mut now: time_t = 0;
// Safe because we give time a valid pointer and can never fail.
time(&mut now as *mut _);
// Safe because we give localtime_r valid pointers and can never fail.
localtime_r(&now, &mut tm as *mut _);
tm
}
}
/// Records a log message with the given details.
///
/// Note that this will fail silently if syslog was not initialized.
///
/// # Arguments
/// * `pri` - The `Priority` (i.e. severity) of the log message.
/// * `fac` - The `Facility` of the log message. Usually `Facility::User` should be used.
/// * `file_name` - Name of the file that generated the log.
/// * `line` - Line number within `file_name` that generated the log.
/// * `args` - The log's message to record, in the form of `format_args!()` return value
///
/// # Examples
///
/// ```
/// # use vmm_sys_util::syslog::{init, log, Priority, Facility};
/// # fn main() {
/// # if let Err(e) = init() {
/// # println!("failed to initiailize syslog: {}", e);
/// # return;
/// # }
/// log(Priority::Error,
/// Facility::User,
/// file!(),
/// line!(),
/// format_args!("hello syslog"));
/// # }
/// ```
#[allow(clippy::redundant_closure)]
pub fn log(pri: Priority, fac: Facility, file_name: &str, line: u32, args: fmt::Arguments) {
const MONTHS: [&str; 12] = [
"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
];
let mut state = lock!();
let mut buf = [0u8; 1024];
if let Some(ref socket) = state.socket {
let tm = get_localtime();
let prifac = (pri as u8) | (fac as u8);
let (res, len) = {
let mut buf_cursor = Cursor::new(&mut buf[..]);
(
write!(
&mut buf_cursor,
"<{}>{} {:02} {:02}:{:02}:{:02} {} {}[{}]: [{}:{}] {}",
prifac,
MONTHS[tm.tm_mon as usize],
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
state.hostname.as_ref().map(|s| s.as_ref()).unwrap_or("-"),
state.proc_name.as_ref().map(|s| s.as_ref()).unwrap_or("-"),
unsafe { syscall(SYS_getpid as c_long) as pid_t },
//getpid(),
file_name,
line,
args
),
buf_cursor.position() as usize,
)
};
if res.is_ok() {
send_buf(&socket, &buf[..len]);
}
}
let (res, len) = {
let mut buf_cursor = Cursor::new(&mut buf[..]);
(
writeln!(&mut buf_cursor, "[{}:{}:{}] {}", pri, file_name, line, args),
buf_cursor.position() as usize,
)
};
if res.is_ok() {
if let Some(ref mut file) = state.file {
let _ = file.write_all(&buf[..len]);
}
if state.stderr {
let _ = stderr().write_all(&buf[..len]);
}
}
}
/// A macro for logging at an arbitrary priority level.
///
/// Note that this will fail silently if syslog was not initialized.
#[macro_export]
macro_rules! log {
($pri:expr, $($args:tt)+) => ({
$crate::syslog::log($pri, $crate::syslog::Facility::User, file!(), line!(), format_args!($($args)+))
})
}
/// A macro for logging an error.
///
/// Note that this will fail silently if syslog was not initialized.
#[macro_export]
macro_rules! error {
($($args:tt)+) => (log!($crate::syslog::Priority::Error, $($args)*))
}
/// A macro for logging a warning.
///
/// Note that this will fail silently if syslog was not initialized.
#[macro_export]
macro_rules! warn {
($($args:tt)+) => (log!($crate::syslog::Priority::Warning, $($args)*))
}
/// A macro for logging info.
///
/// Note that this will fail silently if syslog was not initialized.
#[macro_export]
macro_rules! info {
($($args:tt)+) => (log!($crate::syslog::Priority::Info, $($args)*))
}
/// A macro for logging debug information.
///
/// Note that this will fail silently if syslog was not initialized.
#[macro_export]
macro_rules! debug {
($($args:tt)+) => (log!($crate::syslog::Priority::Debug, $($args)*))
}
#[cfg(test)]
mod tests {
use super::*;
use libc::{shm_open, shm_unlink, O_CREAT, O_EXCL, O_RDWR};
use std::ffi::CStr;
use std::io::{Read, Seek, SeekFrom};
use std::os::unix::io::FromRawFd;
#[test]
fn test_init_syslog() {
init().unwrap();
}
#[test]
fn test_fds() {
init().unwrap();
let mut fds = Vec::new();
push_fds(&mut fds);
assert!(!fds.is_empty());
for fd in fds {
assert!(fd >= 0);
}
}
#[test]
fn test_syslog_log() {
init().unwrap();
log(
Priority::Error,
Facility::User,
file!(),
line!(),
format_args!("hello syslog"),
);
}
#[test]
fn test_proc_name() {
init().unwrap();
log(
Priority::Error,
Facility::User,
file!(),
line!(),
format_args!("before proc name"),
);
set_proc_name("sys_util-test");
log(
Priority::Error,
Facility::User,
file!(),
line!(),
format_args!("after proc name"),
);
}
#[test]
#[allow(clippy::zero_prefixed_literal)]
fn test_syslog_file() {
init().unwrap();
let shm_name = CStr::from_bytes_with_nul(b"/crosvm_shm\0").unwrap();
let mut file = unsafe {
shm_unlink(shm_name.as_ptr());
let fd = shm_open(shm_name.as_ptr(), O_RDWR | O_CREAT | O_EXCL, 0666);
assert!(fd >= 0, "error creating shared memory;");
File::from_raw_fd(fd)
};
let syslog_file = file.try_clone().expect("error cloning shared memory file");
echo_file(Some(syslog_file));
const TEST_STR: &str = "hello shared memory file";
log(
Priority::Error,
Facility::User,
file!(),
line!(),
format_args!("{}", TEST_STR),
);
file.seek(SeekFrom::Start(0))
.expect("error seeking shared memory file");
let mut buf = String::new();
file.read_to_string(&mut buf)
.expect("error reading shared memory file");
assert!(buf.contains(TEST_STR));
}
#[test]
fn test_macros() {
init().unwrap();
error!("this is an error {}", 3);
warn!("this is a warning {}", "uh oh");
info!("this is info {}", true);
debug!("this is debug info {:?}", Some("helpful stuff"));
}
}

View File

@ -1,104 +0,0 @@
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: BSD-3-Clause
use std::ffi::CString;
use std::ffi::OsStr;
use std::ffi::OsString;
use std::fs;
use std::os::unix::ffi::OsStringExt;
use std::path::Path;
use std::path::PathBuf;
use libc;
use crate::{errno_result, Result};
/// Create and remove a temporary directory. The directory will be maintained for the lifetime of
/// the `TempDir` object.
pub struct TempDir {
path: Option<PathBuf>,
}
impl TempDir {
/// Creates a new tempory directory.
/// The directory will be removed when the object goes out of scope.
///
/// # Examples
///
/// ```
/// # use std::path::Path;
/// # use std::path::PathBuf;
/// # use vmm_sys_util::TempDir;
/// # fn test_create_temp_dir() -> Result<(), ()> {
/// let t = TempDir::new("/tmp/testdir").map_err(|_| ())?;
/// assert!(t.as_path().unwrap().exists());
/// # Ok(())
/// # }
/// ```
pub fn new<P: AsRef<OsStr>>(prefix: P) -> Result<TempDir> {
let mut dir_string = prefix.as_ref().to_os_string();
dir_string.push("XXXXXX");
// unwrap this result as the internal bytes can't have a null with a valid path.
let dir_name = CString::new(dir_string.into_vec()).unwrap();
let mut dir_bytes = dir_name.into_bytes_with_nul();
let ret = unsafe {
// Creating the directory isn't unsafe. The fact that it modifies the guts of the path
// is also OK because it only overwrites the last 6 Xs added above.
libc::mkdtemp(dir_bytes.as_mut_ptr() as *mut libc::c_char)
};
if ret.is_null() {
return errno_result();
}
dir_bytes.pop(); // Remove the null becasue from_vec can't handle it.
Ok(TempDir {
path: Some(PathBuf::from(OsString::from_vec(dir_bytes))),
})
}
/// Removes the temporary directory. Calling this is optional as dropping a `TempDir` object
/// will also remove the directory. Calling remove explicitly allows for better error handling.
pub fn remove(mut self) -> Result<()> {
let path = self.path.take();
path.map_or(Ok(()), fs::remove_dir_all)?;
Ok(())
}
/// Returns the path to the tempdir if it is currently valid
pub fn as_path(&self) -> Option<&Path> {
self.path.as_ref().map(|ref p| p.as_path())
}
}
impl Drop for TempDir {
fn drop(&mut self) {
if let Some(ref p) = self.path {
// Nothing can be done here if this returns an error.
let _ = fs::remove_dir_all(p);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn create_dir() {
let t = TempDir::new("/tmp/asdf").unwrap();
let path = t.as_path().unwrap();
assert!(path.exists());
assert!(path.is_dir());
assert!(path.starts_with("/tmp/"));
}
#[test]
fn remove_dir() {
let t = TempDir::new("/tmp/asdf").unwrap();
let path = t.as_path().unwrap().to_owned();
assert!(t.remove().is_ok());
assert!(!path.exists());
}
}

View File

@ -1,155 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2017 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
use std::io::StdinLock;
use std::mem::zeroed;
use std::os::unix::io::RawFd;
use libc::{
c_int, fcntl, isatty, read, tcgetattr, tcsetattr, termios, ECHO, F_GETFL, F_SETFL, ICANON,
ISIG, O_NONBLOCK, STDIN_FILENO, TCSANOW,
};
use crate::errno::{errno_result, Result};
fn modify_mode<F: FnOnce(&mut termios)>(fd: RawFd, f: F) -> Result<()> {
// Safe because we check the return value of isatty.
if unsafe { isatty(fd) } != 1 {
return Ok(());
}
// The following pair are safe because termios gets totally overwritten by tcgetattr and we
// check the return result.
let mut termios: termios = unsafe { zeroed() };
let ret = unsafe { tcgetattr(fd, &mut termios as *mut _) };
if ret < 0 {
return errno_result();
}
let mut new_termios = termios;
f(&mut new_termios);
// Safe because the syscall will only read the extent of termios and we check the return result.
let ret = unsafe { tcsetattr(fd, TCSANOW, &new_termios as *const _) };
if ret < 0 {
return errno_result();
}
Ok(())
}
fn get_flags(fd: RawFd) -> Result<c_int> {
// Safe because no third parameter is expected and we check the return result.
let ret = unsafe { fcntl(fd, F_GETFL) };
if ret < 0 {
return errno_result();
}
Ok(ret)
}
fn set_flags(fd: RawFd, flags: c_int) -> Result<()> {
// Safe because we supply the third parameter and we check the return result.
let ret = unsafe { fcntl(fd, F_SETFL, flags) };
if ret < 0 {
return errno_result();
}
Ok(())
}
/// Trait for file descriptors that are TTYs, according to `isatty(3)`.
///
/// This is marked unsafe because the implementation must promise that the returned RawFd is a valid
/// fd and that the lifetime of the returned fd is at least that of the trait object.
pub unsafe trait Terminal {
/// Gets the file descriptor of the TTY.
fn tty_fd(&self) -> RawFd;
/// Set this terminal's mode to canonical mode (`ICANON | ECHO | ISIG`).
fn set_canon_mode(&self) -> Result<()> {
modify_mode(self.tty_fd(), |t| t.c_lflag |= ICANON | ECHO | ISIG)
}
/// Set this terminal's mode to raw mode (`!(ICANON | ECHO | ISIG)`).
fn set_raw_mode(&self) -> Result<()> {
modify_mode(self.tty_fd(), |t| t.c_lflag &= !(ICANON | ECHO | ISIG))
}
/// Sets the non-blocking mode of this terminal's file descriptor.
///
/// If `non_block` is `true`, then `read_raw` will not block. If `non_block` is `false`, then
/// `read_raw` may block if there is nothing to read.
fn set_non_block(&self, non_block: bool) -> Result<()> {
let old_flags = get_flags(self.tty_fd())?;
let new_flags = if non_block {
old_flags | O_NONBLOCK
} else {
old_flags & !O_NONBLOCK
};
if new_flags != old_flags {
set_flags(self.tty_fd(), new_flags)?
}
Ok(())
}
/// Reads up to `out.len()` bytes from this terminal without any buffering.
///
/// This may block, depending on if non-blocking was enabled with `set_non_block` or if there
/// are any bytes to read. If there is at least one byte that is readable, this will not block.
fn read_raw(&self, out: &mut [u8]) -> Result<usize> {
// Safe because read will only modify the pointer up to the length we give it and we check
// the return result.
let ret = unsafe { read(self.tty_fd(), out.as_mut_ptr() as *mut _, out.len()) };
if ret < 0 {
return errno_result();
}
Ok(ret as usize)
}
}
// Safe because we return a genuine terminal fd that never changes and shares our lifetime.
unsafe impl<'a> Terminal for StdinLock<'a> {
fn tty_fd(&self) -> RawFd {
STDIN_FILENO
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::fs::File;
use std::io;
use std::os::unix::io::AsRawFd;
use std::path::Path;
unsafe impl Terminal for File {
fn tty_fd(&self) -> RawFd {
self.as_raw_fd()
}
}
#[test]
fn test_a_tty() {
let stdin_handle = io::stdin();
let stdin = stdin_handle.lock();
assert!(stdin.set_canon_mode().is_ok());
assert!(stdin.set_raw_mode().is_ok());
assert!(stdin.set_raw_mode().is_ok());
assert!(stdin.set_canon_mode().is_ok());
assert!(stdin.set_non_block(true).is_ok());
let mut out = [0u8; 0];
assert!(stdin.read_raw(&mut out[..]).is_ok());
}
#[test]
fn test_a_non_tty() {
let file = File::open(Path::new("/dev/zero")).unwrap();
assert!(file.set_canon_mode().is_ok());
}
}

View File

@ -1,173 +0,0 @@
// Copyright 2019 Intel Corporation. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0
//
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-clause file.
use std::fs::File;
use std::mem;
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
use std::ptr;
use std::time::Duration;
use libc::{self, timerfd_create, timerfd_gettime, timerfd_settime, CLOCK_MONOTONIC, TFD_CLOEXEC};
use crate::errno::{errno_result, Result};
/// A safe wrapper around a Linux timerfd (man 2 timerfd_create).
pub struct TimerFd(File);
impl TimerFd {
/// Creates a new [`TimerFd`](struct.TimerFd.html).
///
/// The timer is initally disarmed and must be armed by calling [`reset`](fn.reset.html).
pub fn new() -> Result<TimerFd> {
// Safe because this doesn't modify any memory and we check the return value.
let ret = unsafe { timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC) };
if ret < 0 {
return errno_result();
}
// Safe because we uniquely own the file descriptor.
Ok(TimerFd(unsafe { File::from_raw_fd(ret) }))
}
/// Sets the timer to expire after `dur`.
///
/// If `interval` is not `None` it represents the period for repeated expirations after the
/// initial expiration. Otherwise the timer will expire just once. Cancels any existing duration and repeating interval.
pub fn reset(&mut self, dur: Duration, interval: Option<Duration>) -> Result<()> {
// Safe because we are zero-initializing a struct with only primitive member fields.
let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
spec.it_value.tv_sec = dur.as_secs() as libc::time_t;
// nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
let nsec = dur.subsec_nanos() as i32;
spec.it_value.tv_nsec = libc::c_long::from(nsec);
if let Some(int) = interval {
spec.it_interval.tv_sec = int.as_secs() as libc::time_t;
// nsec always fits in i32 because subsec_nanos is defined to be less than one billion.
let nsec = int.subsec_nanos() as i32;
spec.it_interval.tv_nsec = libc::c_long::from(nsec);
}
// Safe because this doesn't modify any memory and we check the return value.
let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
if ret < 0 {
return errno_result();
}
Ok(())
}
/// Waits until the timer expires.
///
/// The return value represents the number of times the timer
/// has expired since the last time `wait` was called. If the timer has not yet expired once
/// this call will block until it does.
pub fn wait(&mut self) -> Result<u64> {
let mut count = 0u64;
// Safe because this will only modify |buf| and we check the return value.
let ret = unsafe {
libc::read(
self.as_raw_fd(),
&mut count as *mut _ as *mut libc::c_void,
mem::size_of_val(&count),
)
};
if ret < 0 {
return errno_result();
}
// The bytes in the buffer are guaranteed to be in native byte-order so we don't need to
// use from_le or from_be.
Ok(count)
}
/// Returns `true` if the timer is currently armed.
pub fn is_armed(&self) -> Result<bool> {
// Safe because we are zero-initializing a struct with only primitive member fields.
let mut spec: libc::itimerspec = unsafe { mem::zeroed() };
// Safe because timerfd_gettime is trusted to only modify `spec`.
let ret = unsafe { timerfd_gettime(self.as_raw_fd(), &mut spec) };
if ret < 0 {
return errno_result();
}
Ok(spec.it_value.tv_sec != 0 || spec.it_value.tv_nsec != 0)
}
/// Disarms the timer.
pub fn clear(&mut self) -> Result<()> {
// Safe because we are zero-initializing a struct with only primitive member fields.
let spec: libc::itimerspec = unsafe { mem::zeroed() };
// Safe because this doesn't modify any memory and we check the return value.
let ret = unsafe { timerfd_settime(self.as_raw_fd(), 0, &spec, ptr::null_mut()) };
if ret < 0 {
return errno_result();
}
Ok(())
}
}
impl AsRawFd for TimerFd {
fn as_raw_fd(&self) -> RawFd {
self.0.as_raw_fd()
}
}
impl FromRawFd for TimerFd {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
TimerFd(File::from_raw_fd(fd))
}
}
impl IntoRawFd for TimerFd {
fn into_raw_fd(self) -> RawFd {
self.0.into_raw_fd()
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::thread::sleep;
use std::time::{Duration, Instant};
#[test]
fn test_one_shot() {
let mut tfd = TimerFd::new().expect("failed to create timerfd");
assert_eq!(tfd.is_armed().unwrap(), false);
let dur = Duration::from_millis(200);
let now = Instant::now();
tfd.reset(dur, None).expect("failed to arm timer");
assert_eq!(tfd.is_armed().unwrap(), true);
let count = tfd.wait().expect("unable to wait for timer");
assert_eq!(count, 1);
assert!(now.elapsed() >= dur);
}
#[test]
fn test_repeating() {
let mut tfd = TimerFd::new().expect("failed to create timerfd");
let dur = Duration::from_millis(200);
let interval = Duration::from_millis(100);
tfd.reset(dur, Some(interval)).expect("failed to arm timer");
sleep(dur * 3);
let count = tfd.wait().expect("unable to wait for timer");
assert!(count >= 5, "count = {}", count);
}
}

View File

@ -1,172 +0,0 @@
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE-BSD-3-Clause file.
//
// SPDX-License-Identifier: BSD-3-Clause
use std::cmp::min;
use std::fs::File;
use std::io::{self, Seek, SeekFrom, Write};
use crate::fallocate;
use crate::FallocateMode;
/// A trait for deallocating space in a file.
pub trait PunchHole {
/// Replace a range of bytes with a hole.
fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()>;
}
impl PunchHole for File {
fn punch_hole(&mut self, offset: u64, length: u64) -> io::Result<()> {
fallocate(self, FallocateMode::PunchHole, true, offset, length as u64)
.map_err(|e| io::Error::from_raw_os_error(e.errno()))
}
}
/// A trait for writing zeroes to a stream.
pub trait WriteZeroes {
/// Write `length` bytes of zeroes to the stream, returning how many bytes were written.
fn write_zeroes(&mut self, length: usize) -> io::Result<usize>;
}
impl<T: PunchHole + Seek + Write> WriteZeroes for T {
fn write_zeroes(&mut self, length: usize) -> io::Result<usize> {
// Try to punch a hole first.
let offset = self.seek(SeekFrom::Current(0))?;
if let Ok(()) = self.punch_hole(offset, length as u64) {
// Advance the seek cursor as if we had done a real write().
self.seek(SeekFrom::Current(length as i64))?;
return Ok(length);
}
// fall back to write()
// punch_hole() failed; fall back to writing a buffer of zeroes
// until we have written up to length.
let buf_size = min(length, 0x10000);
let buf = vec![0u8; buf_size];
let mut nwritten: usize = 0;
while nwritten < length {
let remaining = length - nwritten;
let write_size = min(remaining, buf_size);
nwritten += self.write(&buf[0..write_size])?;
}
Ok(length)
}
}
#[cfg(test)]
#[allow(clippy::unused_io_amount)]
mod tests {
use super::*;
use std::fs::OpenOptions;
use std::io::{Read, Seek, SeekFrom};
use std::path::PathBuf;
use crate::TempDir;
#[test]
fn simple_test() {
let tempdir = TempDir::new("/tmp/write_zeroes_test").unwrap();
let mut path = PathBuf::from(tempdir.as_path().unwrap());
path.push("file");
let mut f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
f.set_len(16384).unwrap();
// Write buffer of non-zero bytes to offset 1234
let orig_data = [0x55u8; 5678];
f.seek(SeekFrom::Start(1234)).unwrap();
f.write(&orig_data).unwrap();
// Read back the data plus some overlap on each side
let mut readback = [0u8; 16384];
f.seek(SeekFrom::Start(0)).unwrap();
f.read(&mut readback).unwrap();
// Bytes before the write should still be 0
for read in readback[0..1234].iter() {
assert_eq!(*read, 0);
}
// Bytes that were just written should be 0x55
for read in readback[1234..(1234 + 5678)].iter() {
assert_eq!(*read, 0x55);
}
// Bytes after the written area should still be 0
for read in readback[(1234 + 5678)..].iter() {
assert_eq!(*read, 0);
}
// Overwrite some of the data with zeroes
f.seek(SeekFrom::Start(2345)).unwrap();
f.write_zeroes(4321).expect("write_zeroes failed");
// Verify seek position after write_zeroes()
assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 2345 + 4321);
// Read back the data and verify that it is now zero
f.seek(SeekFrom::Start(0)).unwrap();
f.read(&mut readback).unwrap();
// Bytes before the write should still be 0
for read in readback[0..1234].iter() {
assert_eq!(*read, 0);
}
// Original data should still exist before the write_zeroes region
for read in readback[1234..2345].iter() {
assert_eq!(*read, 0x55);
}
// The write_zeroes region should now be zero
for read in readback[2345..(2345 + 4321)].iter() {
assert_eq!(*read, 0);
}
// Original data should still exist after the write_zeroes region
for read in readback[(2345 + 4321)..(1234 + 5678)].iter() {
assert_eq!(*read, 0x55);
}
// The rest of the file should still be 0
for read in readback[(1234 + 5678)..].iter() {
assert_eq!(*read, 0);
}
}
#[test]
fn large_write_zeroes() {
let tempdir = TempDir::new("/tmp/write_zeroes_test").unwrap();
let mut path = PathBuf::from(tempdir.as_path().unwrap());
path.push("file");
let mut f = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&path)
.unwrap();
f.set_len(16384).unwrap();
// Write buffer of non-zero bytes
let orig_data = [0x55u8; 0x20000];
f.seek(SeekFrom::Start(0)).unwrap();
f.write(&orig_data).unwrap();
// Overwrite some of the data with zeroes
f.seek(SeekFrom::Start(0)).unwrap();
f.write_zeroes(0x10001).expect("write_zeroes failed");
// Verify seek position after write_zeroes()
assert_eq!(f.seek(SeekFrom::Current(0)).unwrap(), 0x10001);
// Read back the data and verify that it is now zero
let mut readback = [0u8; 0x20000];
f.seek(SeekFrom::Start(0)).unwrap();
f.read(&mut readback).unwrap();
// The write_zeroes region should now be zero
for read in readback[0..0x10001].iter() {
assert_eq!(*read, 0);
}
// Original data should still exist after the write_zeroes region
for read in readback[0x10001..0x20000].iter() {
assert_eq!(*read, 0x55);
}
}
}

View File

@ -16,7 +16,7 @@ pci = { path = "../pci" }
tempfile = ">=3.0.2"
virtio-bindings = { path = "../virtio-bindings" }
vm-allocator = { path = "../vm-allocator" }
vmm-sys-util = { git = "https://github.com/liujing2/vmm-sys-util" }
vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }
[dependencies.vm-memory]
git = "https://github.com/rust-vmm/vm-memory"

View File

@ -18,7 +18,7 @@ qcow = { path = "../qcow" }
linux-loader = { git = "https://github.com/bjzhjing/linux-loader" }
vm-virtio = { path = "../vm-virtio" }
vm-allocator = { path = "../vm-allocator" }
vmm-sys-util = { git = "https://github.com/liujing2/vmm-sys-util" }
vmm-sys-util = { git = "https://github.com/rust-vmm/vmm-sys-util" }
[dependencies.vm-memory]
git = "https://github.com/rust-vmm/vm-memory"