From 2a1dc52c563b1e29dd89fc801fe6f19e5a43cc58 Mon Sep 17 00:00:00 2001 From: Dennis Schwerdel Date: Thu, 11 May 2017 10:47:21 +0200 Subject: [PATCH] Skip root folder on restore & fixed exporting files with long names as tar files --- CHANGELOG.md | 4 +- src/repository/backup.rs | 22 ++++---- src/repository/tarfile.rs | 108 +++++++++++++++++++++++++------------- 3 files changed, 87 insertions(+), 47 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 51183dd..9043e08 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,11 @@ This project follows [semantic versioning](http://semver.org). ### UNRELEASED -* [modifed] Changed order of arguments in `addkey` to match src-dst scheme +* [modified] Changed order of arguments in `addkey` to match src-dst scheme +* [modified] Skip root folder on restore * [fixed] Fixed `addkey` subcommand * [fixed] Fixed reading tar files from stdin +* [fixed] Fixed exporting files with long names as tar files ### v0.3.1 (2017-05-09) diff --git a/src/repository/backup.rs b/src/repository/backup.rs index 84103e1..2cd7d7a 100644 --- a/src/repository/backup.rs +++ b/src/repository/backup.rs @@ -145,25 +145,29 @@ impl Repository { let mut queue = VecDeque::new(); queue.push_back((path.as_ref().to_owned(), inode)); let cache = users::UsersCache::new(); + let mut is_root = true; while let Some((path, mut inode)) = queue.pop_front() { - if let Some(name) = backup.user_names.get(&inode.user) { - if let Some(user) = cache.get_user_by_name(name) { - inode.user = user.uid(); + if inode.file_type != FileType::Directory || !is_root { + if let Some(name) = backup.user_names.get(&inode.user) { + if let Some(user) = cache.get_user_by_name(name) { + inode.user = user.uid(); + } } - } - if let Some(name) = backup.group_names.get(&inode.group) { - if let Some(group) = cache.get_group_by_name(name) { - inode.group = group.gid(); + if let Some(name) = backup.group_names.get(&inode.group) { + if let Some(group) = cache.get_group_by_name(name) { + inode.group = group.gid(); + } } + try!(self.save_inode_at(&inode, &path)); } - try!(self.save_inode_at(&inode, &path)); if inode.file_type == FileType::Directory { - let path = path.join(inode.name); + let path = if is_root { path.to_path_buf() } else { path.join(inode.name) }; for chunks in inode.children.unwrap().values() { let inode = try!(self.get_inode(chunks)); queue.push_back((path.clone(), inode)); } } + is_root = false; } Ok(()) } diff --git a/src/repository/tarfile.rs b/src/repository/tarfile.rs index 85faddd..e9521ea 100644 --- a/src/repository/tarfile.rs +++ b/src/repository/tarfile.rs @@ -5,11 +5,15 @@ use std::path::{Path, PathBuf}; use std::io::{self, Read, Write, Cursor}; use std::fs::File; use std::str; +use std::os::unix::ffi::OsStrExt; use chrono::prelude::*; use tar; +static MAX_NAME_LEN: usize = 99; +static MAX_LINK_LEN: usize = 99; + struct PaxBuilder(Vec); @@ -38,6 +42,8 @@ impl PaxBuilder { trait BuilderExt { fn append_pax_extensions(&mut self, headers: &PaxBuilder) -> Result<(), io::Error>; + fn append_long_name(&mut self, path: &Path) -> Result<(), io::Error>; + fn append_long_link(&mut self, path: &Path) -> Result<(), io::Error>; } impl BuilderExt for tar::Builder { @@ -48,6 +54,24 @@ impl BuilderExt for tar::Builder { header.set_cksum(); self.append(&header, headers.as_bytes()) } + + fn append_long_name(&mut self, path: &Path) -> Result<(), io::Error> { + let mut header = tar::Header::new_gnu(); + let bytes = path.as_os_str().as_bytes(); + header.set_size(bytes.len() as u64); + header.set_entry_type(tar::EntryType::GNULongName); + header.set_cksum(); + self.append(&header, bytes) + } + + fn append_long_link(&mut self, path: &Path) -> Result<(), io::Error> { + let mut header = tar::Header::new_gnu(); + let bytes = path.as_os_str().as_bytes(); + header.set_size(bytes.len() as u64); + header.set_entry_type(tar::EntryType::GNULongLink); + header.set_cksum(); + self.append(&header, bytes) + } } @@ -261,46 +285,56 @@ impl Repository { Ok(try!(tarfile.append_pax_extensions(&pax))) } - fn export_tarfile_recurse(&mut self, backup: &Backup, path: &Path, inode: Inode, tarfile: &mut tar::Builder) -> Result<(), RepositoryError> { - if !inode.xattrs.is_empty() { - try!(self.export_xattrs(&inode, tarfile)); - } - let mut header = tar::Header::new_gnu(); - header.set_size(inode.size); - let path = path.join(inode.name); - try!(header.set_path(&path)); - if let Some(target) = inode.symlink_target { - try!(header.set_link_name(target)); - } - header.set_mode(inode.mode); - header.set_uid(inode.user); - if let Some(name) = backup.user_names.get(&inode.user) { - header.set_username(name).ok(); - } - header.set_gid(inode.group); - if let Some(name) = backup.group_names.get(&inode.group) { - header.set_groupname(name).ok(); - } - header.set_mtime(inode.timestamp as u64); - header.set_entry_type(match inode.file_type { - FileType::File => tar::EntryType::Regular, - FileType::Symlink => tar::EntryType::Symlink, - FileType::Directory => tar::EntryType::Directory - }); - header.set_cksum(); - match inode.data { - None => try!(tarfile.append(&header, Cursor::new(&[]))), - Some(FileData::Inline(data)) => try!(tarfile.append(&header, Cursor::new(data))), - Some(FileData::ChunkedDirect(chunks)) => try!(tarfile.append(&header, self.get_reader(chunks))), - Some(FileData::ChunkedIndirect(chunks)) => { - let chunks = ChunkList::read_from(&try!(self.get_data(&chunks))); - try!(tarfile.append(&header, self.get_reader(chunks))) + fn export_tarfile_recurse(&mut self, backup: &Backup, path: &Path, inode: Inode, tarfile: &mut tar::Builder, skip_root: bool) -> Result<(), RepositoryError> { + let path = if skip_root { path.to_path_buf() } else { path.join(&inode.name) }; + if inode.file_type != FileType::Directory || !skip_root { + if !inode.xattrs.is_empty() { + try!(self.export_xattrs(&inode, tarfile)); + } + let mut header = tar::Header::new_gnu(); + header.set_size(inode.size); + if path.as_os_str().as_bytes().len() >= MAX_NAME_LEN { + try!(tarfile.append_long_name(&path)); + } else { + try!(header.set_path(&path)); + } + if let Some(target) = inode.symlink_target { + if target.len() >= MAX_LINK_LEN { + try!(tarfile.append_long_link(Path::new(&target))); + } else { + try!(header.set_link_name(target)); + } + } + header.set_mode(inode.mode); + header.set_uid(inode.user); + if let Some(name) = backup.user_names.get(&inode.user) { + header.set_username(name).ok(); + } + header.set_gid(inode.group); + if let Some(name) = backup.group_names.get(&inode.group) { + header.set_groupname(name).ok(); + } + header.set_mtime(inode.timestamp as u64); + header.set_entry_type(match inode.file_type { + FileType::File => tar::EntryType::Regular, + FileType::Symlink => tar::EntryType::Symlink, + FileType::Directory => tar::EntryType::Directory + }); + header.set_cksum(); + match inode.data { + None => try!(tarfile.append(&header, Cursor::new(&[]))), + Some(FileData::Inline(data)) => try!(tarfile.append(&header, Cursor::new(data))), + Some(FileData::ChunkedDirect(chunks)) => try!(tarfile.append(&header, self.get_reader(chunks))), + Some(FileData::ChunkedIndirect(chunks)) => { + let chunks = ChunkList::read_from(&try!(self.get_data(&chunks))); + try!(tarfile.append(&header, self.get_reader(chunks))) + } } } if let Some(children) = inode.children { for chunks in children.values() { let inode = try!(self.get_inode(chunks)); - try!(self.export_tarfile_recurse(backup, &path, inode, tarfile)); + try!(self.export_tarfile_recurse(backup, &path, inode, tarfile, false)); } } Ok(()) @@ -310,11 +344,11 @@ impl Repository { let tarfile = tarfile.as_ref(); if tarfile == Path::new("-") { let mut tarfile = tar::Builder::new(io::stdout()); - try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile)); + try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile, true)); try!(tarfile.finish()); } else { let mut tarfile = tar::Builder::new(try!(File::create(tarfile))); - try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile)); + try!(self.export_tarfile_recurse(backup, Path::new(""), inode, &mut tarfile, true)); try!(tarfile.finish()); } Ok(())