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);
|
||||
|
||||
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.local_path.as_os_str().as_bytes());
|
||||
hasher.write_u64(metadata.ctime() as u64);
|
||||
hasher.write_u64(mtime);
|
||||
hasher.write_u64(crtime);
|
||||
@@ -97,14 +95,15 @@ impl LocalOrigin {
|
||||
) -> Result<LocalOrigin, io::Error> {
|
||||
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)
|
||||
.await
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.map(|e| (e.inode, e.hash))
|
||||
.map(|e| (e.inode, e))
|
||||
.collect();
|
||||
|
||||
let mut to_insert: Vec<ActiveModel> = vec![];
|
||||
@@ -113,9 +112,9 @@ impl LocalOrigin {
|
||||
|
||||
for (ino, item) in &snapshot {
|
||||
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)),
|
||||
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))
|
||||
}
|
||||
_ => {}
|
||||
@@ -124,7 +123,7 @@ impl LocalOrigin {
|
||||
|
||||
let fresh_inodes: std::collections::HashSet<i64> =
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -138,6 +137,36 @@ impl LocalOrigin {
|
||||
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 {
|
||||
source: source.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 {
|
||||
let mut hasher = XxHash64::with_seed(5678);
|
||||
hasher.write(path.as_bytes());
|
||||
@@ -412,6 +456,90 @@ impl Filesystem for LocalOrigin {
|
||||
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(
|
||||
&self,
|
||||
_req: &Request,
|
||||
@@ -529,21 +657,7 @@ impl From<&LocalItem> for ActiveModel {
|
||||
impl From<(LocalItemEntity, fs::Metadata)> for LocalItem {
|
||||
fn from((entity, metadata): (LocalItemEntity, fs::Metadata)) -> Self {
|
||||
let local_path = PathBuf::from(&entity.local_path);
|
||||
let parent_inode = {
|
||||
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 parent_inode = LocalOrigin::parent_inode_from_path(&local_path);
|
||||
let local_item = LocalItem {
|
||||
inode: INodeNo(entity.inode as u64),
|
||||
parent_inode,
|
||||
|
||||
Reference in New Issue
Block a user