Add rename of the files with keeping the state
This commit is contained in:
+137
-23
@@ -60,9 +60,7 @@ impl LocalItem {
|
|||||||
.unwrap_or(0);
|
.unwrap_or(0);
|
||||||
|
|
||||||
hasher.write_u64(self.inode.0);
|
hasher.write_u64(self.inode.0);
|
||||||
hasher.write(self.name.as_bytes());
|
|
||||||
hasher.write(self.original_path.as_os_str().as_bytes());
|
hasher.write(self.original_path.as_os_str().as_bytes());
|
||||||
hasher.write(self.local_path.as_os_str().as_bytes());
|
|
||||||
hasher.write_u64(metadata.ctime() as u64);
|
hasher.write_u64(metadata.ctime() as u64);
|
||||||
hasher.write_u64(mtime);
|
hasher.write_u64(mtime);
|
||||||
hasher.write_u64(crtime);
|
hasher.write_u64(crtime);
|
||||||
@@ -97,14 +95,15 @@ impl LocalOrigin {
|
|||||||
) -> Result<LocalOrigin, io::Error> {
|
) -> Result<LocalOrigin, io::Error> {
|
||||||
println!("Initializing the LocalOrigin");
|
println!("Initializing the LocalOrigin");
|
||||||
|
|
||||||
let snapshot = LocalOrigin::build_snapshot(Path::new(&source), Path::new(&destination))?;
|
let mut snapshot =
|
||||||
|
LocalOrigin::build_snapshot(Path::new(&source), Path::new(&destination))?;
|
||||||
|
|
||||||
let db_map: std::collections::HashMap<i64, i64> = Entity::find()
|
let db_items: std::collections::HashMap<i64, Model> = Entity::find()
|
||||||
.all(&client)
|
.all(&client)
|
||||||
.await
|
.await
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|e| (e.inode, e.hash))
|
.map(|e| (e.inode, e))
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut to_insert: Vec<ActiveModel> = vec![];
|
let mut to_insert: Vec<ActiveModel> = vec![];
|
||||||
@@ -113,9 +112,9 @@ impl LocalOrigin {
|
|||||||
|
|
||||||
for (ino, item) in &snapshot {
|
for (ino, item) in &snapshot {
|
||||||
let ino_i64 = ino.0 as i64;
|
let ino_i64 = ino.0 as i64;
|
||||||
match db_map.get(&ino_i64) {
|
match db_items.get(&ino_i64) {
|
||||||
None => to_insert.push(ActiveModel::from(item)),
|
None => to_insert.push(ActiveModel::from(item)),
|
||||||
Some(&db_hash) if db_hash != item.hash as i64 => {
|
Some(db_item) if db_item.hash != item.hash as i64 => {
|
||||||
to_update.push(ActiveModel::from(item))
|
to_update.push(ActiveModel::from(item))
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
@@ -124,7 +123,7 @@ impl LocalOrigin {
|
|||||||
|
|
||||||
let fresh_inodes: std::collections::HashSet<i64> =
|
let fresh_inodes: std::collections::HashSet<i64> =
|
||||||
snapshot.keys().map(|i| i.0 as i64).collect();
|
snapshot.keys().map(|i| i.0 as i64).collect();
|
||||||
for ino in db_map.keys().filter(|i| !fresh_inodes.contains(i)) {
|
for ino in db_items.keys().filter(|i| !fresh_inodes.contains(i)) {
|
||||||
to_delete.push(*ino);
|
to_delete.push(*ino);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -138,6 +137,36 @@ impl LocalOrigin {
|
|||||||
Entity::delete_by_id(ino).exec(&client).await.unwrap();
|
Entity::delete_by_id(ino).exec(&client).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For unchanged items (hash matches), restore virtual paths from DB
|
||||||
|
// so that renames performed in a previous session are preserved.
|
||||||
|
let restorations: Vec<(INodeNo, String, PathBuf)> = db_items
|
||||||
|
.values()
|
||||||
|
.filter_map(|db_item| {
|
||||||
|
let ino = INodeNo(db_item.inode as u64);
|
||||||
|
snapshot
|
||||||
|
.get(&ino)
|
||||||
|
.filter(|item| item.hash as i64 == db_item.hash)
|
||||||
|
.map(|_| {
|
||||||
|
(
|
||||||
|
ino,
|
||||||
|
db_item.name.clone(),
|
||||||
|
PathBuf::from(&db_item.local_path),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
for (_, _, local_path) in &restorations {
|
||||||
|
LocalOrigin::ensure_virtual_dirs(local_path, Path::new(&source), &mut snapshot);
|
||||||
|
}
|
||||||
|
for (ino, name, local_path) in restorations {
|
||||||
|
if let Some(item) = snapshot.get_mut(&ino) {
|
||||||
|
item.name = name;
|
||||||
|
item.parent_inode = LocalOrigin::parent_inode_from_path(&local_path);
|
||||||
|
item.local_path = local_path;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let local_origin = LocalOrigin {
|
let local_origin = LocalOrigin {
|
||||||
source: source.into(),
|
source: source.into(),
|
||||||
destination: destination.into(),
|
destination: destination.into(),
|
||||||
@@ -290,6 +319,21 @@ impl LocalOrigin {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parent_inode_from_path(local_path: &Path) -> INodeNo {
|
||||||
|
let components: Vec<_> = local_path.components().collect();
|
||||||
|
if components.len() <= 1 {
|
||||||
|
return INodeNo::ROOT;
|
||||||
|
}
|
||||||
|
let mut parent_path = String::new();
|
||||||
|
for (i, comp) in components[..components.len() - 1].iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
parent_path.push('/');
|
||||||
|
}
|
||||||
|
parent_path.push_str(comp.as_os_str().to_str().unwrap_or_default());
|
||||||
|
}
|
||||||
|
LocalOrigin::virtual_inode(&parent_path)
|
||||||
|
}
|
||||||
|
|
||||||
fn virtual_inode(path: &str) -> INodeNo {
|
fn virtual_inode(path: &str) -> INodeNo {
|
||||||
let mut hasher = XxHash64::with_seed(5678);
|
let mut hasher = XxHash64::with_seed(5678);
|
||||||
hasher.write(path.as_bytes());
|
hasher.write(path.as_bytes());
|
||||||
@@ -412,6 +456,90 @@ impl Filesystem for LocalOrigin {
|
|||||||
reply.ok();
|
reply.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn rename(
|
||||||
|
&self,
|
||||||
|
_req: &Request,
|
||||||
|
parent: INodeNo,
|
||||||
|
name: &std::ffi::OsStr,
|
||||||
|
newparent: INodeNo,
|
||||||
|
newname: &std::ffi::OsStr,
|
||||||
|
_flags: fuser::RenameFlags,
|
||||||
|
reply: fuser::ReplyEmpty,
|
||||||
|
) {
|
||||||
|
let name_str = match name.to_str() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
reply.error(Errno::EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let newname_str = match newname.to_str() {
|
||||||
|
Some(s) => s,
|
||||||
|
None => {
|
||||||
|
reply.error(Errno::EINVAL);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut files = self.files.lock().unwrap();
|
||||||
|
|
||||||
|
let item_ino = match files
|
||||||
|
.iter()
|
||||||
|
.find(|(_, v)| v.parent_inode == parent && v.name == name_str)
|
||||||
|
{
|
||||||
|
Some((ino, _)) => *ino,
|
||||||
|
None => {
|
||||||
|
reply.error(Errno::ENOENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_local_path = if newparent == INodeNo::ROOT {
|
||||||
|
PathBuf::from(newname_str)
|
||||||
|
} else {
|
||||||
|
match files.get(&newparent) {
|
||||||
|
Some(dir) => dir.local_path.join(newname_str),
|
||||||
|
None => {
|
||||||
|
reply.error(Errno::ENOENT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let new_parent_inode = LocalOrigin::parent_inode_from_path(&new_local_path);
|
||||||
|
|
||||||
|
let item = files.get_mut(&item_ino).unwrap();
|
||||||
|
item.name = newname_str.to_string();
|
||||||
|
item.local_path = new_local_path.clone();
|
||||||
|
item.parent_inode = new_parent_inode;
|
||||||
|
|
||||||
|
let inode_i64 = item_ino.0 as i64;
|
||||||
|
let new_name_owned = newname_str.to_string();
|
||||||
|
let new_local_path_str = new_local_path.to_string_lossy().into_owned();
|
||||||
|
let client = self.client.clone();
|
||||||
|
|
||||||
|
drop(files);
|
||||||
|
|
||||||
|
tokio::runtime::Builder::new_current_thread()
|
||||||
|
.enable_time()
|
||||||
|
.build()
|
||||||
|
.unwrap()
|
||||||
|
.block_on(async move {
|
||||||
|
use sea_orm::ActiveValue::Set;
|
||||||
|
ActiveModel {
|
||||||
|
inode: Set(inode_i64),
|
||||||
|
name: Set(new_name_owned),
|
||||||
|
local_path: Set(new_local_path_str),
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
.update(&client)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
});
|
||||||
|
|
||||||
|
reply.ok();
|
||||||
|
}
|
||||||
|
|
||||||
fn lookup(
|
fn lookup(
|
||||||
&self,
|
&self,
|
||||||
_req: &Request,
|
_req: &Request,
|
||||||
@@ -529,21 +657,7 @@ impl From<&LocalItem> for ActiveModel {
|
|||||||
impl From<(LocalItemEntity, fs::Metadata)> for LocalItem {
|
impl From<(LocalItemEntity, fs::Metadata)> for LocalItem {
|
||||||
fn from((entity, metadata): (LocalItemEntity, fs::Metadata)) -> Self {
|
fn from((entity, metadata): (LocalItemEntity, fs::Metadata)) -> Self {
|
||||||
let local_path = PathBuf::from(&entity.local_path);
|
let local_path = PathBuf::from(&entity.local_path);
|
||||||
let parent_inode = {
|
let parent_inode = LocalOrigin::parent_inode_from_path(&local_path);
|
||||||
let components: Vec<_> = local_path.components().collect();
|
|
||||||
if components.len() <= 1 {
|
|
||||||
INodeNo::ROOT
|
|
||||||
} else {
|
|
||||||
let mut parent_path = String::new();
|
|
||||||
for (i, comp) in components[..components.len() - 1].iter().enumerate() {
|
|
||||||
if i > 0 {
|
|
||||||
parent_path.push('/');
|
|
||||||
}
|
|
||||||
parent_path.push_str(comp.as_os_str().to_str().unwrap_or_default());
|
|
||||||
}
|
|
||||||
LocalOrigin::virtual_inode(&parent_path)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let local_item = LocalItem {
|
let local_item = LocalItem {
|
||||||
inode: INodeNo(entity.inode as u64),
|
inode: INodeNo(entity.inode as u64),
|
||||||
parent_inode,
|
parent_inode,
|
||||||
|
|||||||
Reference in New Issue
Block a user