mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-05 06:27:38 +00:00
Extending filesystem support for hermit-os
This commit is contained in:
parent
384d26fc7e
commit
aa11bf6f0a
@ -48,6 +48,11 @@ impl FileDesc {
|
|||||||
pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
|
pub fn set_nonblocking(&self, _nonblocking: bool) -> io::Result<()> {
|
||||||
unsupported()
|
unsupported()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn fstat(&self, stat: *mut abi::stat) -> io::Result<()> {
|
||||||
|
cvt(unsafe { abi::fstat(self.fd.as_raw_fd(), stat) })?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Read for &'a FileDesc {
|
impl<'a> Read for &'a FileDesc {
|
||||||
|
@ -2,11 +2,14 @@ use super::abi::{self, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_W
|
|||||||
use super::fd::FileDesc;
|
use super::fd::FileDesc;
|
||||||
use crate::ffi::{CStr, OsString};
|
use crate::ffi::{CStr, OsString};
|
||||||
use crate::fmt;
|
use crate::fmt;
|
||||||
use crate::hash::{Hash, Hasher};
|
|
||||||
use crate::io::{self, Error, ErrorKind};
|
use crate::io::{self, Error, ErrorKind};
|
||||||
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
|
use crate::io::{BorrowedCursor, IoSlice, IoSliceMut, SeekFrom};
|
||||||
|
use crate::mem;
|
||||||
|
use crate::os::hermit::ffi::OsStringExt;
|
||||||
use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
|
use crate::os::hermit::io::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, RawFd};
|
||||||
use crate::path::{Path, PathBuf};
|
use crate::path::{Path, PathBuf};
|
||||||
|
use crate::ptr;
|
||||||
|
use crate::sync::Arc;
|
||||||
use crate::sys::common::small_c_string::run_path_with_cstr;
|
use crate::sys::common::small_c_string::run_path_with_cstr;
|
||||||
use crate::sys::cvt;
|
use crate::sys::cvt;
|
||||||
use crate::sys::time::SystemTime;
|
use crate::sys::time::SystemTime;
|
||||||
@ -18,12 +21,44 @@ pub use crate::sys_common::fs::{copy, try_exists};
|
|||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct File(FileDesc);
|
pub struct File(FileDesc);
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FileAttr {
|
||||||
|
stat_val: stat_struct,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FileAttr(!);
|
impl FileAttr {
|
||||||
|
fn from_stat(stat_val: stat_struct) -> Self {
|
||||||
|
Self { stat_val }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ReadDir(!);
|
// all DirEntry's will have a reference to this struct
|
||||||
|
struct InnerReadDir {
|
||||||
|
dirp: FileDesc,
|
||||||
|
root: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct DirEntry(!);
|
pub struct ReadDir {
|
||||||
|
inner: Arc<InnerReadDir>,
|
||||||
|
end_of_stream: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ReadDir {
|
||||||
|
fn new(inner: InnerReadDir) -> Self {
|
||||||
|
Self { inner: Arc::new(inner), end_of_stream: false }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct DirEntry {
|
||||||
|
dir: Arc<InnerReadDir>,
|
||||||
|
entry: dirent_min,
|
||||||
|
name: OsString,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct dirent_min {
|
||||||
|
d_ino: u64,
|
||||||
|
d_type: u32,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct OpenOptions {
|
pub struct OpenOptions {
|
||||||
@ -41,72 +76,78 @@ pub struct OpenOptions {
|
|||||||
#[derive(Copy, Clone, Debug, Default)]
|
#[derive(Copy, Clone, Debug, Default)]
|
||||||
pub struct FileTimes {}
|
pub struct FileTimes {}
|
||||||
|
|
||||||
pub struct FilePermissions(!);
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
pub struct FilePermissions {
|
||||||
|
mode: u32,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct FileType(!);
|
#[derive(Copy, Clone, Eq, Debug)]
|
||||||
|
pub struct FileType {
|
||||||
|
mode: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for FileType {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.mode == other.mode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl core::hash::Hash for FileType {
|
||||||
|
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.mode.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct DirBuilder {}
|
pub struct DirBuilder {
|
||||||
|
mode: u32,
|
||||||
|
}
|
||||||
|
|
||||||
impl FileAttr {
|
impl FileAttr {
|
||||||
pub fn size(&self) -> u64 {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn perm(&self) -> FilePermissions {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_type(&self) -> FileType {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn modified(&self) -> io::Result<SystemTime> {
|
pub fn modified(&self) -> io::Result<SystemTime> {
|
||||||
self.0
|
Ok(SystemTime::new(self.stat_val.st_mtime, self.stat_val.st_mtime_nsec))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn accessed(&self) -> io::Result<SystemTime> {
|
pub fn accessed(&self) -> io::Result<SystemTime> {
|
||||||
self.0
|
Ok(SystemTime::new(self.stat_val.st_atime, self.stat_val.st_atime_nsec))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn created(&self) -> io::Result<SystemTime> {
|
pub fn created(&self) -> io::Result<SystemTime> {
|
||||||
self.0
|
Ok(SystemTime::new(self.stat_val.st_ctime, self.stat_val.st_ctime_nsec))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for FileAttr {
|
pub fn size(&self) -> u64 {
|
||||||
fn clone(&self) -> FileAttr {
|
self.stat_val.st_size as u64
|
||||||
self.0
|
}
|
||||||
|
pub fn perm(&self) -> FilePermissions {
|
||||||
|
FilePermissions { mode: (self.stat_val.st_mode) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_type(&self) -> FileType {
|
||||||
|
let masked_mode = self.stat_val.st_mode & S_IFMT;
|
||||||
|
let mode = match masked_mode {
|
||||||
|
S_IFDIR => DT_DIR,
|
||||||
|
S_IFLNK => DT_LNK,
|
||||||
|
S_IFREG => DT_REG,
|
||||||
|
_ => DT_UNKNOWN,
|
||||||
|
};
|
||||||
|
FileType { mode: mode }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FilePermissions {
|
impl FilePermissions {
|
||||||
pub fn readonly(&self) -> bool {
|
pub fn readonly(&self) -> bool {
|
||||||
self.0
|
// check if any class (owner, group, others) has write permission
|
||||||
|
self.mode & 0o222 == 0
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_readonly(&mut self, _readonly: bool) {
|
pub fn set_readonly(&mut self, _readonly: bool) {
|
||||||
self.0
|
unimplemented!()
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for FilePermissions {
|
#[allow(dead_code)]
|
||||||
fn clone(&self) -> FilePermissions {
|
pub fn mode(&self) -> u32 {
|
||||||
self.0
|
self.mode as u32
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PartialEq for FilePermissions {
|
|
||||||
fn eq(&self, _other: &FilePermissions) -> bool {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for FilePermissions {}
|
|
||||||
|
|
||||||
impl fmt::Debug for FilePermissions {
|
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,49 +158,21 @@ impl FileTimes {
|
|||||||
|
|
||||||
impl FileType {
|
impl FileType {
|
||||||
pub fn is_dir(&self) -> bool {
|
pub fn is_dir(&self) -> bool {
|
||||||
self.0
|
self.mode == DT_DIR
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_file(&self) -> bool {
|
pub fn is_file(&self) -> bool {
|
||||||
self.0
|
self.mode == DT_REG
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_symlink(&self) -> bool {
|
pub fn is_symlink(&self) -> bool {
|
||||||
self.0
|
self.mode == DT_LNK
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for FileType {
|
|
||||||
fn clone(&self) -> FileType {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Copy for FileType {}
|
|
||||||
|
|
||||||
impl PartialEq for FileType {
|
|
||||||
fn eq(&self, _other: &FileType) -> bool {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Eq for FileType {}
|
|
||||||
|
|
||||||
impl Hash for FileType {
|
|
||||||
fn hash<H: Hasher>(&self, _h: &mut H) {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for FileType {
|
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
self.0
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for ReadDir {
|
impl fmt::Debug for ReadDir {
|
||||||
fn fmt(&self, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
self.0
|
// This will only be called from std::fs::ReadDir, which will add a "ReadDir()" frame.
|
||||||
|
// Thus the result will be e g 'ReadDir("/home")'
|
||||||
|
fmt::Debug::fmt(&*self.inner.root, f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,25 +180,105 @@ impl Iterator for ReadDir {
|
|||||||
type Item = io::Result<DirEntry>;
|
type Item = io::Result<DirEntry>;
|
||||||
|
|
||||||
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
fn next(&mut self) -> Option<io::Result<DirEntry>> {
|
||||||
self.0
|
if self.end_of_stream {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
loop {
|
||||||
|
// As of POSIX.1-2017, readdir() is not required to be thread safe; only
|
||||||
|
// readdir_r() is. However, readdir_r() cannot correctly handle platforms
|
||||||
|
// with unlimited or variable NAME_MAX. Many modern platforms guarantee
|
||||||
|
// thread safety for readdir() as long an individual DIR* is not accessed
|
||||||
|
// concurrently, which is sufficient for Rust.
|
||||||
|
let entry_ptr = match abi::readdir(self.inner.dirp.as_raw_fd()) {
|
||||||
|
abi::DirectoryEntry::Invalid(e) => {
|
||||||
|
// We either encountered an error, or reached the end. Either way,
|
||||||
|
// the next call to next() should return None.
|
||||||
|
self.end_of_stream = true;
|
||||||
|
|
||||||
|
return Some(Err(Error::from_raw_os_error(e)));
|
||||||
|
}
|
||||||
|
abi::DirectoryEntry::Valid(ptr) => {
|
||||||
|
if ptr.is_null() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptr
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
macro_rules! offset_ptr {
|
||||||
|
($entry_ptr:expr, $field:ident) => {{
|
||||||
|
const OFFSET: isize = {
|
||||||
|
let delusion = MaybeUninit::<dirent>::uninit();
|
||||||
|
let entry_ptr = delusion.as_ptr();
|
||||||
|
unsafe {
|
||||||
|
ptr::addr_of!((*entry_ptr).$field)
|
||||||
|
.cast::<u8>()
|
||||||
|
.offset_from(entry_ptr.cast::<u8>())
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if true {
|
||||||
|
// Cast to the same type determined by the else branch.
|
||||||
|
$entry_ptr.byte_offset(OFFSET).cast::<_>()
|
||||||
|
} else {
|
||||||
|
#[allow(deref_nullptr)]
|
||||||
|
{
|
||||||
|
ptr::addr_of!((*ptr::null::<dirent>()).$field)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
}
|
||||||
|
|
||||||
|
// d_name is NOT guaranteed to be null-terminated.
|
||||||
|
let name_bytes = core::slice::from_raw_parts(
|
||||||
|
offset_ptr!(entry_ptr, d_name) as *const u8,
|
||||||
|
*offset_ptr!(entry_ptr, d_namelen) as usize,
|
||||||
|
)
|
||||||
|
.to_vec();
|
||||||
|
|
||||||
|
if name_bytes == b"." || name_bytes == b".." {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let name = OsString::from_vec(name_bytes);
|
||||||
|
|
||||||
|
let entry = dirent_min {
|
||||||
|
d_ino: *offset_ptr!(entry_ptr, d_ino),
|
||||||
|
d_type: *offset_ptr!(entry_ptr, d_type),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(Ok(DirEntry { entry, name: name, dir: Arc::clone(&self.inner) }));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DirEntry {
|
impl DirEntry {
|
||||||
pub fn path(&self) -> PathBuf {
|
pub fn path(&self) -> PathBuf {
|
||||||
self.0
|
self.dir.root.join(self.file_name_os_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_name(&self) -> OsString {
|
pub fn file_name(&self) -> OsString {
|
||||||
self.0
|
self.file_name_os_str().to_os_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn metadata(&self) -> io::Result<FileAttr> {
|
pub fn metadata(&self) -> io::Result<FileAttr> {
|
||||||
self.0
|
lstat(&self.path())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_type(&self) -> io::Result<FileType> {
|
pub fn file_type(&self) -> io::Result<FileType> {
|
||||||
self.0
|
Ok(FileType { mode: self.entry.d_type })
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn ino(&self) -> u64 {
|
||||||
|
self.entry.d_ino
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_name_os_str(&self) -> &OsStr {
|
||||||
|
self.name.as_os_str()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -288,7 +381,9 @@ impl File {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
pub fn file_attr(&self) -> io::Result<FileAttr> {
|
||||||
Err(Error::from_raw_os_error(22))
|
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
|
||||||
|
self.0.fstat(&mut stat_val)?;
|
||||||
|
Ok(FileAttr::from_stat(stat_val))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fsync(&self) -> io::Result<()> {
|
pub fn fsync(&self) -> io::Result<()> {
|
||||||
@ -357,11 +452,18 @@ impl File {
|
|||||||
|
|
||||||
impl DirBuilder {
|
impl DirBuilder {
|
||||||
pub fn new() -> DirBuilder {
|
pub fn new() -> DirBuilder {
|
||||||
DirBuilder {}
|
DirBuilder { mode: 0o777 }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mkdir(&self, _p: &Path) -> io::Result<()> {
|
pub fn mkdir(&self, path: &Path) -> io::Result<()> {
|
||||||
unsupported()
|
run_path_with_cstr(path, |path| {
|
||||||
|
cvt(unsafe { abi::mkdir(path.as_ptr(), self.mode) }).map(|_| ())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn set_mode(&mut self, mode: u32) {
|
||||||
|
self.mode = mode as u32;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -416,8 +518,12 @@ impl FromRawFd for File {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn readdir(_p: &Path) -> io::Result<ReadDir> {
|
pub fn readdir(path: &Path) -> io::Result<ReadDir> {
|
||||||
unsupported()
|
let fd_raw = run_path_with_cstr(path, |path| cvt(unsafe { abi::opendir(path.as_ptr()) }))?;
|
||||||
|
let fd = unsafe { FileDesc::from_raw_fd(fd_raw as i32) };
|
||||||
|
let root = path.to_path_buf();
|
||||||
|
let inner = InnerReadDir { dirp: fd, root };
|
||||||
|
Ok(ReadDir::new(inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unlink(path: &Path) -> io::Result<()> {
|
pub fn unlink(path: &Path) -> io::Result<()> {
|
||||||
@ -428,12 +534,12 @@ pub fn rename(_old: &Path, _new: &Path) -> io::Result<()> {
|
|||||||
unsupported()
|
unsupported()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_perm(_p: &Path, perm: FilePermissions) -> io::Result<()> {
|
pub fn set_perm(_p: &Path, _perm: FilePermissions) -> io::Result<()> {
|
||||||
match perm.0 {}
|
Err(Error::from_raw_os_error(22))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn rmdir(_p: &Path) -> io::Result<()> {
|
pub fn rmdir(path: &Path) -> io::Result<()> {
|
||||||
unsupported()
|
run_path_with_cstr(path, |path| cvt(unsafe { abi::rmdir(path.as_ptr()) }).map(|_| ()))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
|
pub fn remove_dir_all(_path: &Path) -> io::Result<()> {
|
||||||
@ -453,12 +559,20 @@ pub fn link(_original: &Path, _link: &Path) -> io::Result<()> {
|
|||||||
unsupported()
|
unsupported()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn stat(_p: &Path) -> io::Result<FileAttr> {
|
pub fn stat(path: &Path) -> io::Result<FileAttr> {
|
||||||
unsupported()
|
run_path_with_cstr(path, |path| {
|
||||||
|
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
|
||||||
|
cvt(unsafe { abi::stat(path.as_ptr(), &mut stat_val) })?;
|
||||||
|
Ok(FileAttr::from_stat(stat_val))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn lstat(_p: &Path) -> io::Result<FileAttr> {
|
pub fn lstat(path: &Path) -> io::Result<FileAttr> {
|
||||||
unsupported()
|
run_path_with_cstr(path, |path| {
|
||||||
|
let mut stat_val: stat_struct = unsafe { mem::zeroed() };
|
||||||
|
cvt(unsafe { abi::lstat(path.as_ptr(), &mut stat_val) })?;
|
||||||
|
Ok(FileAttr::from_stat(stat_val))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
|
pub fn canonicalize(_p: &Path) -> io::Result<PathBuf> {
|
||||||
|
@ -18,6 +18,12 @@ impl Timespec {
|
|||||||
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
|
Timespec { t: timespec { tv_sec: 0, tv_nsec: 0 } }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fn new(tv_sec: i64, tv_nsec: i64) -> Timespec {
|
||||||
|
assert!(tv_nsec >= 0 && tv_nsec < NSEC_PER_SEC as i64);
|
||||||
|
// SAFETY: The assert above checks tv_nsec is within the valid range
|
||||||
|
Timespec { t: timespec { tv_sec: tv_sec, tv_nsec: tv_nsec } }
|
||||||
|
}
|
||||||
|
|
||||||
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
|
fn sub_timespec(&self, other: &Timespec) -> Result<Duration, Duration> {
|
||||||
if self >= other {
|
if self >= other {
|
||||||
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
|
Ok(if self.t.tv_nsec >= other.t.tv_nsec {
|
||||||
@ -195,6 +201,10 @@ pub struct SystemTime(Timespec);
|
|||||||
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
|
pub const UNIX_EPOCH: SystemTime = SystemTime(Timespec::zero());
|
||||||
|
|
||||||
impl SystemTime {
|
impl SystemTime {
|
||||||
|
pub fn new(tv_sec: i64, tv_nsec: i64) -> SystemTime {
|
||||||
|
SystemTime(Timespec::new(tv_sec, tv_nsec))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn now() -> SystemTime {
|
pub fn now() -> SystemTime {
|
||||||
let mut time: Timespec = Timespec::zero();
|
let mut time: Timespec = Timespec::zero();
|
||||||
let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) };
|
let _ = unsafe { abi::clock_gettime(CLOCK_REALTIME, core::ptr::addr_of_mut!(time.t)) };
|
||||||
|
Loading…
Reference in New Issue
Block a user