Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docs/_docs/user-guide/eldritch.md
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,32 @@ The **file.follow** method will call `fn(line)` for any new `line` that is added
file.follow('/home/bob/.bash_history', print)
```

### file.get_times

`file.get_times(path: str) -> Dict`

The **file.get_times** method gets all timestamp metadata of a file/directory.

On *Windows/Unix*, the **Created, Accessed, Modified** times are returned. On *Unix* targets specifically, the **Changed** time is also returned.

These times are returned as an integer epoch (SECONDS), as in the number of seconds that elapsed from **Jan 1 1970**

Example, with **1781444321** being the unix epoch for **Sunday, June 14, 2026 at 1:38:41 PM UTC**:

```python
file.mkdir("./testdir")
print(file.get_times("./file"))
```

```json
{
"atime": 1781444321,
"crtime": 1781444321,
"ctime": 1781444321,
"mtime": 1781444321
}
```

### file.is_dir

`file.is_dir(path: str) -> bool`
Expand Down
18 changes: 18 additions & 0 deletions implants/lib/eldritch/stdlib/eldritch-libfile/src/fake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,24 @@ impl FileLibrary for FileLibraryFake {
Ok(())
}

fn get_times(&self, path: String) -> Result<BTreeMap<String, i64>, String> {
// ensure file existence
if !self.exists(path) {
return Err("File doesn't exist".to_string());
}

// create btree
let mut map = BTreeMap::new();
map.insert("mtime".to_string(), 0);
map.insert("atime".to_string(), 0);
map.insert("crtime".to_string(), 0);

#[cfg(unix)]
map.insert("ctime".to_string(), 0);

Ok(map)
}

fn is_dir(&self, path: String) -> Result<bool, String> {
let mut root = self.root.lock();
let parts = Self::normalize_path(&path);
Expand Down
18 changes: 18 additions & 0 deletions implants/lib/eldritch/stdlib/eldritch-libfile/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,24 @@ pub trait FileLibrary {
fn_val: Value,
) -> Result<(), String>; // fn is reserved

#[eldritch_method]
/// Gets the times of a file
/// Modified, Created, and Accessed work on most systems, and for UNIX systems changed time is also outputted.
///
/// **Parameters**
/// - `path` (`str`): The file to get times from
///
/// **Returns**
/// - `Dict`: The dictionary of files
/// - `mtime` (`int`)
/// - `atime` (`int`)
/// - `crtime` (`int`)
/// - `ctime` (unix only, `int`)
///
/// **Errors**
/// - Returns an error upon failure of getting time of file
fn get_times(&self, path: String) -> Result<BTreeMap<String, i64>, String>;

#[eldritch_method]
/// Checks if the path exists and is a directory.
///
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#[cfg(feature = "stdlib")]
extern crate alloc;

#[cfg(feature = "stdlib")]
use alloc::collections::BTreeMap;

#[cfg(feature = "stdlib")]
use alloc::string::String;

#[cfg(feature = "stdlib")]
use std::{fs::{exists, metadata}, time::{SystemTime, UNIX_EPOCH}};

#[cfg(feature = "stdlib")]
fn time_to_epoch(time: SystemTime) -> Result<i64, String> {
// get the distance so the time can be represented as an epoch
// epoch is in seconds
let epoch_secs = time.duration_since(UNIX_EPOCH).map_err(|e| e.to_string())?.as_secs().cast_signed();
Ok(epoch_secs)
}

#[cfg(feature = "stdlib")]
fn fetch_times(path: String) -> Result<BTreeMap<String, i64>, String> {
// check if exists
if !exists(&path).map_err(|e| e.to_string())? {
return Err(alloc::format!("Could not locate {}", path));
}

// open the file
let meta = metadata(path).map_err(|e| e.to_string())?;

// create the output
let mut dict = BTreeMap::new();
dict.insert("mtime".to_string(), time_to_epoch(meta.modified().map_err(|e| e.to_string())?)?);
dict.insert("atime".to_string(), time_to_epoch(meta.accessed().map_err(|e| e.to_string())?)?);
dict.insert("crtime".to_string(), time_to_epoch(meta.created().map_err(|e| e.to_string())?)?);

// change time is only available on posix systems
#[cfg(unix)]
use std::os::unix::fs::MetadataExt;
#[cfg(unix)]
dict.insert("ctime".to_string(), meta.ctime());

Ok(dict)
}

#[cfg(feature = "stdlib")]
pub fn get_times(path: String) -> Result<BTreeMap<String, i64>, String> {
Ok(fetch_times(path)?)
}

#[cfg(not(feature = "stdlib"))]
pub fn get_times(path: alloc::string::String) -> Result<alloc::collections::BTreeMap<alloc::string::String, i64>, alloc::string::String> {
return Err("stdlib required");
}
5 changes: 5 additions & 0 deletions implants/lib/eldritch/stdlib/eldritch-libfile/src/std/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub mod timestomp_impl;
pub mod tmp_dir_impl;
pub mod write_binary_impl;
pub mod write_impl;
pub mod get_times_impl;

#[derive(Debug, Default)]
#[eldritch_library_impl(FileLibrary)]
Expand Down Expand Up @@ -82,6 +83,10 @@ impl FileLibrary for StdFileLibrary {
list_impl::list(path)
}

fn get_times(&self, path: String) -> Result<BTreeMap<String, i64>, String> {
get_times_impl::get_times(path)
}

fn list_named_pipes(&self, detailed: Option<bool>) -> Result<Value, String> {
list_named_pipes_impl::list_named_pipes(detailed)
}
Expand Down