mirror of
https://github.com/OMGeeky/gdriver2.git
synced 2026-01-06 19:49:47 +01:00
implement kinda working readdir
This commit is contained in:
14
Cargo.lock
generated
14
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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(())
|
||||
|
||||
@@ -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<i64>,
|
||||
pub mime_type: String,
|
||||
pub kind: String,
|
||||
pub kind: FileKind,
|
||||
pub md5_checksum: Option<String>,
|
||||
pub parents: Option<Vec<String>>,
|
||||
pub parents: Vec<String>,
|
||||
pub trashed: Option<bool>,
|
||||
pub created_time: Option<DateTime<Utc>>,
|
||||
pub modified_time: Option<DateTime<Utc>>,
|
||||
pub viewed_by_me_time: Option<DateTime<Utc>>,
|
||||
}
|
||||
|
||||
impl FileData {
|
||||
pub(crate) fn into_meta(self) -> Result<Metadata> {
|
||||
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<HttpsConnector<HttpConnector>>,
|
||||
changes_start_page_token: Option<String>,
|
||||
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::<GoogleDrive>())
|
||||
.field("changes_start_page_token", &self.changes_start_page_token)
|
||||
.field("root_alt_id", &self.root_alt_id)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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?;
|
||||
|
||||
@@ -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<DriveId, Vec<DriveId>>,
|
||||
@@ -14,6 +16,12 @@ pub struct PathResolver {
|
||||
children: HashMap<DriveId, Vec<ReadDirResult>>,
|
||||
}
|
||||
|
||||
impl PathResolver {
|
||||
pub(crate) fn get_children(&self, id: &DriveId) -> Result<&Vec<ReadDirResult>> {
|
||||
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<Self> {
|
||||
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(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,16 +87,24 @@ impl GDriverService for GdriverServer {
|
||||
context: Context,
|
||||
id: DriveId,
|
||||
) -> StdResult<Vec<ReadDirResult>, 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<Vec<ReadDirResult>, 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(
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<InodeAttributes> 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,
|
||||
|
||||
@@ -13,6 +13,7 @@ type Result<T> = StdResult<T, Box<dyn Error>>;
|
||||
#[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;
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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<u8>, Vec<u8>>,
|
||||
pub permissions: u16,
|
||||
pub extra_attributes: BTreeMap<Vec<u8>, Vec<u8>>,
|
||||
}
|
||||
|
||||
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<Metadata> {
|
||||
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)?)
|
||||
|
||||
@@ -24,7 +24,7 @@ pub trait GDriverService {
|
||||
) -> StdResult<Vec<ReadDirResult>, GetFileListError>;
|
||||
async fn list_files_in_directory_with_offset(
|
||||
id: DriveId,
|
||||
offset: u64,
|
||||
offset: usize,
|
||||
) -> StdResult<Vec<ReadDirResult>, 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)]
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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;
|
||||
|
||||
39
gdriver-common/src/time_utils.rs
Normal file
39
gdriver-common/src/time_utils.rs
Normal file
@@ -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<TIMESTAMP> {
|
||||
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<Utc>) -> prelude::Result<TIMESTAMP> {
|
||||
let unix_epoch: DateTime<Utc> = 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(),
|
||||
),
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user