diff --git a/Cargo.lock b/Cargo.lock index d9e0cfe..1a899ea 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -510,6 +510,7 @@ dependencies = [ "google-drive3", "lazy_static", "serde", + "serde_json", "tarpc", "thiserror", "tokio", @@ -526,12 +527,14 @@ dependencies = [ "futures", "futures-sink", "gdriver-common", + "lazy_static", "libc", "serde", "tarpc", "thiserror", "tokio", "tracing", + "uzers", ] [[package]] @@ -539,6 +542,7 @@ name = "gdriver-common" version = "0.1.0" dependencies = [ "anyhow", + "chrono", "confique", "directories", "futures", @@ -1985,6 +1989,16 @@ dependencies = [ "percent-encoding 2.3.1", ] +[[package]] +name = "uzers" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76d283dc7e8c901e79e32d077866eaf599156cbf427fffa8289aecc52c5c3f63" +dependencies = [ + "libc", + "log", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/gdriver-backend/Cargo.toml b/gdriver-backend/Cargo.toml index 5329494..28b0a6d 100644 --- a/gdriver-backend/Cargo.toml +++ b/gdriver-backend/Cargo.toml @@ -12,10 +12,11 @@ serde.workspace = true tarpc.workspace = true futures.workspace = true chrono.workspace = true -lazy_static = "1.4.0" +lazy_static.workspace = true thiserror = "1.0.56" google-drive3 = "5.0.4" const_format = "0.2" +serde_json = "1.0.115" [dependencies.gdriver-common] path = "../gdriver-common" diff --git a/gdriver-backend/src/drive.rs b/gdriver-backend/src/drive.rs index 873ff27..313ad13 100644 --- a/gdriver-backend/src/drive.rs +++ b/gdriver-backend/src/drive.rs @@ -3,6 +3,8 @@ use std::collections::HashMap; use crate::drive::google_drive::GoogleDrive; use crate::path_resolver::PathResolver; use chrono::{DateTime, Utc}; +use gdriver_common::drive_structure::meta::{write_metadata_file, write_metadata_file_to_path}; +use gdriver_common::ipc::gdriver_service::ReadDirResult; use crate::prelude::*; mod google_drive; @@ -36,19 +38,29 @@ impl Drive { #[instrument(skip(self))] pub async fn get_all_file_metas(&mut self) -> Result<()> { - if self.offline_mode { - info!("Offline mode, skipping get_all_file_metas"); - //TODO: load from local storage - return Ok(()); - } let has_existing_token = self.google_drive.has_local_change_token().await; + //TODO: show an error when offline and no local data exists if !has_existing_token { - //only get start token & data if we shouldn't have it + //only get start token & data if this is the first time & we don't have it self.google_drive.get_change_start_token().await?; - let x = self.google_drive.get_all_file_metas().await?; - dbg!(&x); + let files = self.google_drive.get_all_file_metas().await?; + + self.path_resolver.reset()?; + for file in files { + for parent in file.parents.clone() { + let relation_data = ReadDirResult { + id: file.id.clone().into(), + name: file.name.clone(), + kind: file.kind.clone(), + }; + self.path_resolver + .add_relationship(parent.into(), relation_data)?; + } + let meta = file.into_meta()?; + write_metadata_file(&meta)?; + } } else { - //TODO: get file metas from local storage + self.path_resolver.load_from_disk()?; } Ok(()) diff --git a/gdriver-backend/src/drive/google_drive.rs b/gdriver-backend/src/drive/google_drive.rs index a8d123b..a712c9f 100644 --- a/gdriver-backend/src/drive/google_drive.rs +++ b/gdriver-backend/src/drive/google_drive.rs @@ -1,6 +1,8 @@ use crate::prelude::*; use chrono::{DateTime, Utc}; use const_format::formatcp; +use gdriver_common::drive_structure::meta::{FileKind, FileState, Metadata}; +use gdriver_common::time_utils::datetime_to_timestamp; use gdriver_common::{ipc::gdriver_service::SETTINGS, prelude::*}; use google_drive3::api::File; use google_drive3::{ @@ -14,32 +16,60 @@ use std::any::type_name; use std::fmt::{Debug, Display, Formatter}; use tokio::fs; -const FIELDS_FILE: &'static str = "id, name, size, mimeType, kind, md5Checksum, parents, trashed, createdTime, modifiedTime, viewedByMeTime"; +const FIELDS_FILE: &'static str = "id, name, size, mimeType, kind, md5Checksum, parents, trashed, createdTime, modifiedTime, viewedByMeTime, capabilities"; #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Serialize, Deserialize, Clone, Hash)] pub struct FileData { pub id: String, pub name: String, pub size: Option, pub mime_type: String, - pub kind: String, + pub kind: FileKind, pub md5_checksum: Option, - pub parents: Option>, + pub parents: Vec, pub trashed: Option, pub created_time: Option>, pub modified_time: Option>, pub viewed_by_me_time: Option>, } +impl FileData { + pub(crate) fn into_meta(self) -> Result { + let last_modified = datetime_to_timestamp(self.modified_time.unwrap_or_default())?; + Ok(Metadata { + id: self.id.into(), + kind: self.kind, + size: self.size.unwrap_or_default() as u64, + last_accessed: datetime_to_timestamp(self.viewed_by_me_time.unwrap_or_default())?, + last_modified, + extra_attributes: Default::default(), + state: FileState::MetadataOnly, + permissions: 0, //TODO: parse permissions + last_metadata_changed: last_modified, + }) + } +} + impl FileData { fn convert_from_api_file(file: File) -> Self { + let kind = file.kind.unwrap_or_default(); + info!( + "Converting file with id {:?} with parent: {:?}", + file.id, file.parents + ); + let kind = match kind.as_str() { + "drive#file" => FileKind::File, + "drive#folder" => FileKind::Directory, + "drive#link" => FileKind::Symlink, + _ => todo!("Handle kind: {}", kind), + }; Self { id: file.id.unwrap_or_default(), name: file.name.unwrap_or_default(), size: file.size, mime_type: file.mime_type.unwrap_or_default(), - kind: file.kind.unwrap_or_default(), + kind, md5_checksum: file.md5_checksum, - parents: file.parents, + parents: file.parents.unwrap_or(vec![ROOT_ID.0.clone()]), trashed: file.trashed, created_time: file.created_time, modified_time: file.modified_time, @@ -55,6 +85,7 @@ const FIELDS_CHANGE: &str = formatcp!( pub struct GoogleDrive { hub: DriveHub>, changes_start_page_token: Option, + root_alt_id: DriveId, } impl GoogleDrive { @@ -79,6 +110,10 @@ impl GoogleDrive { body.files .unwrap_or_default() .into_iter() + .map(|mut f| { + self.map_in_file(Some(&mut f)); + f + }) .map(FileData::convert_from_api_file), ); } else { @@ -117,13 +152,31 @@ impl GoogleDrive { ); let hub = DriveHub::new(http_client, auth); - let drive = GoogleDrive { + let mut drive = GoogleDrive { hub, changes_start_page_token: None, + root_alt_id: ROOT_ID.clone(), }; - trace!("Successfully initialized {}", drive); + info!("Updating ROOT alt"); + drive.update_alt_root().await?; + info!("Updated ROOT alt to {}", drive.root_alt_id); + trace!("Successfully initialized {:?}", drive); Ok(drive) } + async fn update_alt_root(&mut self) -> Result<()> { + let (response, body) = self + .hub + .files() + .get(ROOT_ID.as_ref()) + .param("fields", "id") + .doit() + .await?; + if response.status().is_success() { + self.root_alt_id = body.id.unwrap_or(ROOT_ID.to_string()).into(); + } + + Ok(()) + } #[instrument] pub(crate) async fn ping(&self) -> Result<()> { let (response, body) = self @@ -183,9 +236,13 @@ impl GoogleDrive { return Err("Could not get changes".into()); } } + changes + .iter_mut() + .for_each(|change| self.map_id_in_change(change)); trace!("Got {} changes", changes.len()); Ok(changes) } + async fn set_change_start_token(&mut self, token: String) -> Result<()> { info!("Setting start page token: {}", token); fs::write(SETTINGS.get_changes_file_path(), token.clone()).await?; @@ -245,13 +302,41 @@ impl GoogleDrive { } } //endregion -} + //region map alt_root_id to ROOT_ID + fn map_id_in_change(&self, i: &mut Change) { + i.file_id.as_mut().map(|id| self.map_id(id)); + let file = i.file.as_mut(); + self.map_in_file(file); + } + fn map_in_file(&self, file: Option<&mut File>) { + file.map(|f| { + f.parents.as_mut().map(|parents| { + parents + .iter_mut() + .inspect(|i| println!("parent: {i}")) + .for_each(|id| self.map_id(id)) + }); + f.id.as_mut().map(|id| self.map_id(id)); + }); + } + + fn map_id(&self, id: &mut String) { + if self.root_alt_id.0.eq(id) { + info!("replacing {id} with {}", ROOT_ID.as_ref()); + *id = ROOT_ID.0.clone(); + } else { + info!("{id} did not match {}", self.root_alt_id); + } + } + //endregion +} //region debug & display traits impl Debug for GoogleDrive { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct(type_name::()) .field("changes_start_page_token", &self.changes_start_page_token) + .field("root_alt_id", &self.root_alt_id) .finish() } } diff --git a/gdriver-backend/src/main.rs b/gdriver-backend/src/main.rs index c31cab7..fbb26ec 100644 --- a/gdriver-backend/src/main.rs +++ b/gdriver-backend/src/main.rs @@ -24,7 +24,7 @@ async fn main() -> Result<()> { SETTINGS.initialize_dirs()?; let root_meta_file = SETTINGS.get_metadata_file_path(&ROOT_ID); let root_meta = meta::Metadata::root(); - meta::write_metadata_file(&root_meta_file, &root_meta)?; + meta::write_metadata_file_to_path(&root_meta_file, &root_meta)?; // sample::main().await?; service::start().await?; diff --git a/gdriver-backend/src/path_resolver.rs b/gdriver-backend/src/path_resolver.rs index f6d5ce6..290f3b4 100644 --- a/gdriver-backend/src/path_resolver.rs +++ b/gdriver-backend/src/path_resolver.rs @@ -1,12 +1,14 @@ use crate::drive::Drive; use crate::prelude::*; -use gdriver_common::ipc::gdriver_service::ReadDirResult; +use gdriver_common::ipc::gdriver_service::{ReadDirResult, SETTINGS}; use gdriver_common::path_resolve_error::PathResolveError; use gdriver_common::prelude::*; +use serde::{Deserialize, Serialize}; use std::collections::HashMap; +use std::fs::File; use std::path::Path; -#[derive(Eq, PartialEq, Debug, Clone)] +#[derive(Eq, PartialEq, Debug, Clone, Serialize, Deserialize)] pub struct PathResolver { /// A map of children to their parents parents: HashMap>, @@ -14,6 +16,12 @@ pub struct PathResolver { children: HashMap>, } +impl PathResolver { + pub(crate) fn get_children(&self, id: &DriveId) -> Result<&Vec> { + self.children.get(id).ok_or("Item with ID not found".into()) + } +} + impl PathResolver { pub fn new() -> Self { Self { @@ -47,11 +55,14 @@ impl PathResolver { None } - async fn update_from_drive(&mut self, drive: &Drive) -> Result<()> { - todo!() + pub fn reset(&mut self) -> Result<()> { + self.parents.clear(); + self.children.clear(); + self.write_to_disk()?; + Ok(()) } - /// Add a relationship between a parent and a child - pub(crate) fn add_relationship(&mut self, parent: DriveId, entry: ReadDirResult) { + /// Add a relationship between a parent and a child and write to disk + pub(crate) fn add_relationship(&mut self, parent: DriveId, entry: ReadDirResult) -> Result<()> { match self.parents.get_mut(&entry.id) { Some(x) => x.push(parent.clone()), None => { @@ -64,14 +75,39 @@ impl PathResolver { self.children.insert(parent.clone(), vec![entry.clone()]); } } + self.write_to_disk()?; + Ok(()) } - /// Remove the relationship between a parent and a child - pub(crate) fn remove_relationship(&mut self, parent: DriveId, entry: ReadDirResult) { + /// Remove the relationship between a parent and a child and write to disk + pub(crate) fn remove_relationship( + &mut self, + parent: DriveId, + entry: ReadDirResult, + ) -> Result<()> { self.parents .get_mut(&entry.id) .map(|x| x.retain(|e| e != &parent)); self.children .get_mut(&parent) .map(|x| x.retain(|e| e.id != entry.id)); + self.write_to_disk()?; + Ok(()) + } + + pub fn write_to_disk(&self) -> Result<()> { + let path = SETTINGS.get_path_resolver_file_path(); + let reader = File::create(path)?; + Ok(serde_json::to_writer_pretty(reader, self)?) + } + pub fn read_from_disk() -> Result { + let path = SETTINGS.get_path_resolver_file_path(); + let reader = File::open(path)?; + Ok(serde_json::from_reader(reader)?) + } + pub fn load_from_disk(&mut self) -> Result<()> { + let other = Self::read_from_disk()?; + self.parents = other.parents; + self.children = other.children; + Ok(()) } } diff --git a/gdriver-backend/src/service.rs b/gdriver-backend/src/service.rs index 13b0e09..7da4199 100644 --- a/gdriver-backend/src/service.rs +++ b/gdriver-backend/src/service.rs @@ -87,16 +87,24 @@ impl GDriverService for GdriverServer { context: Context, id: DriveId, ) -> StdResult, GetFileListError> { - Err(GetFileListError::Other) + self.list_files_in_directory_with_offset(context, id, 0) + .await } - + #[instrument(skip(self, _context))] async fn list_files_in_directory_with_offset( self, - context: Context, + _context: Context, id: DriveId, - offset: u64, + offset: usize, ) -> StdResult, GetFileListError> { - Err(GetFileListError::Other) + let drive = self.drive.lock().await; + info!("Listing files in dir"); + let children = drive + .path_resolver + .get_children(&id) + .map_err(|_| GetFileListError::NotFound)? + .clone(); + Ok(children.into_iter().skip(offset).collect()) } async fn mark_file_as_deleted( diff --git a/gdriver-client/Cargo.toml b/gdriver-client/Cargo.toml index 0851930..f32e1bc 100644 --- a/gdriver-client/Cargo.toml +++ b/gdriver-client/Cargo.toml @@ -10,6 +10,7 @@ tarpc.workspace = true tokio.workspace = true tracing.workspace = true serde.workspace = true +lazy_static.workspace = true anyhow = "1.0" futures-sink = "0.3.30" fuser = "0.14.0" @@ -17,6 +18,7 @@ bimap = "0.6" libc = "0.2.152" futures = "0.3" thiserror = "1.0.56" +uzers = "0.11" [dependencies.gdriver-common] path = "../gdriver-common" diff --git a/gdriver-client/src/filesystem.rs b/gdriver-client/src/filesystem.rs index 13ea991..7626180 100644 --- a/gdriver-client/src/filesystem.rs +++ b/gdriver-client/src/filesystem.rs @@ -10,6 +10,7 @@ use gdriver_common::drive_structure::drive_id::ROOT_ID; use gdriver_common::ipc::gdriver_service::errors::GDriverServiceError; use gdriver_common::ipc::gdriver_service::GDriverServiceClient; use gdriver_common::ipc::gdriver_service::SETTINGS; +use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::ffi::{OsStr, OsString}; @@ -22,6 +23,15 @@ mod macros; //TODO2: Decide if this is a good TTL const TTL: Duration = Duration::from_secs(2); +const GROUP_NAME: &str = "gdriver2"; +lazy_static! { + pub static ref USER_ID: u32 = uzers::get_current_uid(); + pub static ref GDRIVER_GROUP_ID: u32 = uzers::get_group_by_name(GROUP_NAME) + .expect(&format!( + "Please create the group '{GROUP_NAME}' and add the user to it." + )) + .gid(); +} type Inode = u64; @@ -321,7 +331,7 @@ mod utils { let res = send_request!(fs.gdriver_client.list_files_in_directory_with_offset( current_context(), id, - offset as u64 + offset as usize ))? .map_err(GDriverServiceError::from)?; Ok(res) diff --git a/gdriver-client/src/filesystem/attributes.rs b/gdriver-client/src/filesystem/attributes.rs index 15acedb..1ee4b63 100644 --- a/gdriver-client/src/filesystem/attributes.rs +++ b/gdriver-client/src/filesystem/attributes.rs @@ -1,10 +1,13 @@ +use crate::filesystem::{GDRIVER_GROUP_ID, USER_ID}; use crate::prelude::*; use fuser::FileType; -use gdriver_common::drive_structure::meta::{read_metadata_file, FileKind, Metadata}; +use gdriver_common::drive_structure::meta::{read_metadata_file, FileKind, Metadata, TIMESTAMP}; +use gdriver_common::time_utils; +use gdriver_common::time_utils::time_from_system_time; use std::collections::BTreeMap; use std::os::raw::c_int; use std::path::Path; -use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use std::time::SystemTime; use tarpc::serde::{Deserialize, Serialize}; type Inode = u64; @@ -78,36 +81,17 @@ fn time_now() -> (i64, u32) { time_from_system_time(&SystemTime::now()) } -fn system_time_from_time(secs: i64, nsecs: u32) -> SystemTime { - if secs >= 0 { - UNIX_EPOCH + Duration::new(secs as u64, nsecs) - } else { - UNIX_EPOCH - Duration::new((-secs) as u64, nsecs) - } -} - -fn time_from_system_time(system_time: &SystemTime) -> (i64, u32) { - // Convert to signed 64-bit time with epoch at 0 - match system_time.duration_since(UNIX_EPOCH) { - Ok(duration) => (duration.as_secs() as i64, duration.subsec_nanos()), - Err(before_epoch_error) => ( - -(before_epoch_error.duration().as_secs() as i64), - before_epoch_error.duration().subsec_nanos(), - ), - } -} - #[derive(Serialize, Deserialize)] pub(crate) struct InodeAttributes { pub inode: Inode, pub open_file_handles: u64, // Ref count of open file handles to this inode pub size: u64, - pub last_accessed: (i64, u32), - pub last_modified: (i64, u32), - pub last_metadata_changed: (i64, u32), + pub last_accessed: TIMESTAMP, + pub last_modified: TIMESTAMP, + pub last_metadata_changed: TIMESTAMP, pub kind: FileKind, // Permissions and special mode bits - pub mode: u16, + pub permissions: u16, pub hardlinks: u32, pub uid: u32, pub gid: u32, @@ -126,11 +110,11 @@ pub(crate) fn read_inode_attributes_from_metadata( last_modified: metadata.last_modified, last_metadata_changed: metadata.last_metadata_changed, kind: metadata.kind, - mode: metadata.mode, - hardlinks: metadata.hardlinks, - uid: metadata.uid, - gid: metadata.gid, - xattrs: metadata.xattrs, + permissions: metadata.permissions, + hardlinks: 0, + uid: *USER_ID, + gid: *GDRIVER_GROUP_ID, + xattrs: metadata.extra_attributes, } } pub(crate) fn read_inode_attributes_from_meta_file( @@ -152,15 +136,21 @@ impl From for fuser::FileAttr { ino: attrs.inode, size: attrs.size, blocks: (attrs.size + BLOCK_SIZE - 1) / BLOCK_SIZE, - atime: system_time_from_time(attrs.last_accessed.0, attrs.last_accessed.1), - mtime: system_time_from_time(attrs.last_modified.0, attrs.last_modified.1), - ctime: system_time_from_time( + atime: time_utils::system_time_from_timestamp( + attrs.last_accessed.0, + attrs.last_accessed.1, + ), + mtime: time_utils::system_time_from_timestamp( + attrs.last_modified.0, + attrs.last_modified.1, + ), + ctime: time_utils::system_time_from_timestamp( attrs.last_metadata_changed.0, attrs.last_metadata_changed.1, ), crtime: SystemTime::UNIX_EPOCH, kind: attrs.kind.into_ft(), - perm: attrs.mode, + perm: attrs.permissions, nlink: attrs.hardlinks, uid: attrs.uid, gid: attrs.gid, diff --git a/gdriver-client/src/main.rs b/gdriver-client/src/main.rs index f8b6c76..9b53bc7 100644 --- a/gdriver-client/src/main.rs +++ b/gdriver-client/src/main.rs @@ -13,6 +13,7 @@ type Result = StdResult>; #[tokio::main] async fn main() -> Result<()> { gdriver_common::tracing_setup::init_tracing(); + check_setup()?; // service::start().await?; let mount_options = &[MountOption::RW]; let (tx, rx) = channel(1); @@ -26,6 +27,16 @@ async fn main() -> Result<()> { .await?; Ok(()) } + +fn check_setup() -> Result<()> { + // let _ = std::env::var("GOOGLE_APPLICATION_CREDENTIALS") + // .map_err(|_| "GOOGLE_APPLICATION_CREDENTIALS env var not set")?; + let _ = &*filesystem::GDRIVER_GROUP_ID; + let _ = &*filesystem::USER_ID; + + Ok(()) +} + pub mod prelude; mod sample; diff --git a/gdriver-common/Cargo.toml b/gdriver-common/Cargo.toml index 0775de5..b015ad9 100644 --- a/gdriver-common/Cargo.toml +++ b/gdriver-common/Cargo.toml @@ -12,6 +12,7 @@ tarpc.workspace = true tokio.workspace = true futures.workspace = true lazy_static.workspace = true +chrono.workspace = true confique = { version = "0.2" } thiserror = "1.0" anyhow = "1.0" diff --git a/gdriver-common/src/drive_structure/meta.rs b/gdriver-common/src/drive_structure/meta.rs index c0ff431..53cedd7 100644 --- a/gdriver-common/src/drive_structure/meta.rs +++ b/gdriver-common/src/drive_structure/meta.rs @@ -1,38 +1,38 @@ +use crate::ipc::gdriver_service::SETTINGS; use crate::prelude::*; use serde::{Deserialize, Serialize}; use std::{collections::BTreeMap, fs::File, path::Path}; +/// Timestamp is a tuple of (seconds, nanoseconds) +/// +/// This is a duration since the Unix epoch in seconds + nanoseconds. pub type TIMESTAMP = (i64, u32); #[derive(Ord, PartialOrd, Eq, PartialEq, Debug, Serialize, Deserialize, Clone, Hash)] pub struct Metadata { + pub id: DriveId, pub state: FileState, pub size: u64, pub last_accessed: TIMESTAMP, pub last_modified: TIMESTAMP, pub last_metadata_changed: TIMESTAMP, pub kind: FileKind, - pub mode: u16, - pub hardlinks: u32, - pub uid: u32, - pub gid: u32, - pub xattrs: BTreeMap, Vec>, + pub permissions: u16, + pub extra_attributes: BTreeMap, Vec>, } impl Metadata { pub fn root() -> Self { Self { + id: ROOT_ID.clone(), state: FileState::Root, size: 0, last_accessed: (0, 0), last_modified: (0, 0), last_metadata_changed: (0, 0), kind: FileKind::Directory, - mode: 0, - hardlinks: 0, - uid: 0, - gid: 0, - xattrs: Default::default(), + permissions: 0, + extra_attributes: Default::default(), } } } @@ -42,7 +42,11 @@ pub fn read_metadata_file(path: &Path) -> Result { let reader = File::open(path)?; Ok(serde_json::from_reader(reader)?) } -pub fn write_metadata_file(path: &Path, metadata: &Metadata) -> Result<()> { +pub fn write_metadata_file(metadata: &Metadata) -> Result<()> { + let path = SETTINGS.get_metadata_file_path(&metadata.id); + write_metadata_file_to_path(&path, metadata) +} +pub fn write_metadata_file_to_path(path: &Path, metadata: &Metadata) -> Result<()> { debug!("Writing metadata file: {:?}", path); let reader = File::create(path)?; Ok(serde_json::to_writer(reader, metadata)?) diff --git a/gdriver-common/src/ipc/gdriver_service.rs b/gdriver-common/src/ipc/gdriver_service.rs index f1dd5fa..d6ae887 100644 --- a/gdriver-common/src/ipc/gdriver_service.rs +++ b/gdriver-common/src/ipc/gdriver_service.rs @@ -24,7 +24,7 @@ pub trait GDriverService { ) -> StdResult, GetFileListError>; async fn list_files_in_directory_with_offset( id: DriveId, - offset: u64, + offset: usize, ) -> StdResult, GetFileListError>; async fn mark_file_as_deleted(id: DriveId) -> StdResult<(), MarkFileAsDeletedError>; async fn mark_file_for_keeping_local( @@ -148,6 +148,8 @@ pub mod errors { pub enum GetFileListError { #[error("Other")] Other, + #[error("Element with ID not found")] + NotFound, } #[derive(Debug, Serialize, Deserialize, thiserror::Error)] diff --git a/gdriver-common/src/ipc/gdriver_settings.rs b/gdriver-common/src/ipc/gdriver_settings.rs index 3358003..369c92a 100644 --- a/gdriver-common/src/ipc/gdriver_settings.rs +++ b/gdriver-common/src/ipc/gdriver_settings.rs @@ -45,6 +45,9 @@ impl GDriverSettings { pub fn get_changes_file_path(&self) -> PathBuf { self.data_path.join("changes.txt") } + pub fn get_path_resolver_file_path(&self) -> PathBuf { + self.data_path.join("relations.json") + } pub fn get_metadata_file_path(&self, id: &DriveId) -> PathBuf { self.metadata_path.join(id.as_ref()).with_extension("meta") diff --git a/gdriver-common/src/lib.rs b/gdriver-common/src/lib.rs index 39b52c0..7763448 100644 --- a/gdriver-common/src/lib.rs +++ b/gdriver-common/src/lib.rs @@ -7,4 +7,5 @@ pub mod drive_structure; pub mod ipc; pub mod path_resolve_error; pub mod project_dirs; +pub mod time_utils; pub mod tracing_setup; diff --git a/gdriver-common/src/time_utils.rs b/gdriver-common/src/time_utils.rs new file mode 100644 index 0000000..55361e2 --- /dev/null +++ b/gdriver-common/src/time_utils.rs @@ -0,0 +1,39 @@ +use crate::drive_structure::meta::TIMESTAMP; +use crate::prelude; +use chrono::offset::Utc; +use chrono::DateTime; +use chrono::TimeZone; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; + +pub fn system_time_to_timestamp(time: SystemTime) -> prelude::Result { + let secs = time.duration_since(UNIX_EPOCH)?.as_secs() as i64; + let nsecs = time.duration_since(UNIX_EPOCH)?.subsec_nanos(); + Ok((secs, nsecs)) +} + +pub fn datetime_to_timestamp(time: DateTime) -> prelude::Result { + let unix_epoch: DateTime = Utc.with_ymd_and_hms(1970, 1, 1, 0, 0, 0).unwrap(); + let timestamp = time.signed_duration_since(unix_epoch); + let secs = timestamp.num_seconds(); + let nsecs = timestamp.subsec_nanos() as u32; + Ok((secs, nsecs)) +} + +pub fn system_time_from_timestamp(secs: i64, nsecs: u32) -> SystemTime { + if secs >= 0 { + UNIX_EPOCH + Duration::new(secs as u64, nsecs) + } else { + UNIX_EPOCH - Duration::new((-secs) as u64, nsecs) + } +} + +pub fn time_from_system_time(system_time: &SystemTime) -> (i64, u32) { + // Convert to signed 64-bit time with epoch at 0 + match system_time.duration_since(UNIX_EPOCH) { + Ok(duration) => (duration.as_secs() as i64, duration.subsec_nanos()), + Err(before_epoch_error) => ( + -(before_epoch_error.duration().as_secs() as i64), + before_epoch_error.duration().subsec_nanos(), + ), + } +}