mirror of https://github.com/dswd/zvault
Added support for block/char devices
This commit is contained in:
parent
8d45176146
commit
9231800f3b
|
@ -6,6 +6,7 @@ This project follows [semantic versioning](http://semver.org).
|
|||
### UNRELEASED
|
||||
* [added] Added `copy` subcommand
|
||||
* [added] Added support for xattrs in fuse mount
|
||||
* [added] Added support for block/char devices
|
||||
* [modified] Also documenting common flags in subcommands
|
||||
* [modified] Using repository aliases (**conversion needed**)
|
||||
* [modified] Remote path must be absolute
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
# Mounted locations and pseudo filesystems
|
||||
/cdrom
|
||||
/dev
|
||||
lost+found
|
||||
/mnt
|
||||
/sys
|
||||
|
|
|
@ -306,11 +306,15 @@ The `FileType` describes the type of an inode.
|
|||
- `Directory` means a directory that does not contain data but might have
|
||||
children
|
||||
- `Symlink` means a symlink that points to a target
|
||||
- `BlockDevice` means a block device
|
||||
- `CharDevice` mean a character device
|
||||
|
||||
FileType {
|
||||
File => 0,
|
||||
Directory => 1,
|
||||
Symlink => 2
|
||||
Symlink => 2,
|
||||
BlockDevice => 3,
|
||||
CharDevice => 4
|
||||
}
|
||||
|
||||
|
||||
|
@ -432,6 +436,9 @@ as well as the whole subtree (including all children recursively). `cum_size` is
|
|||
the sum of all inode data sizes plus 1000 bytes for each inode (for encoded
|
||||
metadata). `cum_dirs` and `cum_files` is the count of directories and
|
||||
non-directories (symlinks and regular files).
|
||||
The `xattrs` contains a mapping of all extended attributes of the inode. And
|
||||
`device` contains a tuple with the major and minor device id if the inode is a
|
||||
block or character device.
|
||||
|
||||
Inode {
|
||||
name: string => 0,
|
||||
|
@ -447,6 +454,8 @@ non-directories (symlinks and regular files).
|
|||
cum_size: int => 12,
|
||||
cum_dirs: int => 13,
|
||||
cum_files: int => 14
|
||||
xattrs: {string => bytes}? => 15,
|
||||
device: (int, int)? => 16
|
||||
}
|
||||
|
||||
This structure is encoded with the following field default values:
|
||||
|
|
|
@ -153,6 +153,10 @@ pub fn format_inode_one_line(inode: &Inode) -> String {
|
|||
FileType::Directory => format!("{:25}\t{} entries", format!("{}/", inode.name), inode.children.as_ref().map(|c| c.len()).unwrap_or(0)),
|
||||
FileType::File => format!("{:25}\t{:>10}\t{}", inode.name, to_file_size(inode.size), Local.timestamp(inode.timestamp, 0).to_rfc2822()),
|
||||
FileType::Symlink => format!("{:25}\t -> {}", inode.name, inode.symlink_target.as_ref().map(|s| s as &str).unwrap_or("?")),
|
||||
FileType::BlockDevice | FileType::CharDevice => {
|
||||
let device = inode.device.unwrap_or((0, 0));
|
||||
format!("{:25}\t{:12}\t{}:{}", inode.name, inode.file_type, device.0, device.1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,8 @@ fn convert_file_type(kind: FileType) -> fuse::FileType {
|
|||
FileType::Directory => fuse::FileType::Directory,
|
||||
FileType::File => fuse::FileType::RegularFile,
|
||||
FileType::Symlink => fuse::FileType::Symlink,
|
||||
FileType::BlockDevice => fuse::FileType::BlockDevice,
|
||||
FileType::CharDevice => fuse::FileType::CharDevice
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -112,7 +114,7 @@ impl FuseInode {
|
|||
nlink: 1,
|
||||
uid: uid,
|
||||
gid: gid,
|
||||
rdev: 0,
|
||||
rdev: self.inode.device.map_or(0, |(major, minor)| (major << 8) + minor),
|
||||
flags: 0
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,14 +2,17 @@ use ::prelude::*;
|
|||
|
||||
use filetime::{self, FileTime};
|
||||
use xattr;
|
||||
use libc;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::fs::{self, File, Permissions};
|
||||
use std::os::linux::fs::MetadataExt;
|
||||
use std::os::unix::fs::{PermissionsExt, symlink};
|
||||
use std::os::unix::fs::{FileTypeExt, PermissionsExt, MetadataExt as UnixMetadataExt, symlink};
|
||||
use std::io::{self, Read, Write};
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
use std::fmt;
|
||||
use std::ffi;
|
||||
|
||||
|
||||
quick_error!{
|
||||
|
@ -63,19 +66,25 @@ quick_error!{
|
|||
pub enum FileType {
|
||||
File,
|
||||
Directory,
|
||||
Symlink
|
||||
Symlink,
|
||||
BlockDevice,
|
||||
CharDevice
|
||||
}
|
||||
serde_impl!(FileType(u8) {
|
||||
File => 0,
|
||||
Directory => 1,
|
||||
Symlink => 2
|
||||
Symlink => 2,
|
||||
BlockDevice => 3,
|
||||
CharDevice => 4
|
||||
});
|
||||
impl fmt::Display for FileType {
|
||||
fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||
match *self {
|
||||
FileType::File => write!(format, "file"),
|
||||
FileType::Directory => write!(format, "directory"),
|
||||
FileType::Symlink => write!(format, "symlink")
|
||||
FileType::Symlink => write!(format, "symlink"),
|
||||
FileType::BlockDevice => write!(format, "block device"),
|
||||
FileType::CharDevice => write!(format, "char device")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -109,7 +118,8 @@ pub struct Inode {
|
|||
pub cum_size: u64,
|
||||
pub cum_dirs: usize,
|
||||
pub cum_files: usize,
|
||||
pub xattrs: BTreeMap<String, msgpack::Bytes>
|
||||
pub xattrs: BTreeMap<String, msgpack::Bytes>,
|
||||
pub device: Option<(u32, u32)>
|
||||
}
|
||||
impl Default for Inode {
|
||||
fn default() -> Self {
|
||||
|
@ -127,7 +137,8 @@ impl Default for Inode {
|
|||
cum_size: 0,
|
||||
cum_dirs: 0,
|
||||
cum_files: 0,
|
||||
xattrs: BTreeMap::new()
|
||||
xattrs: BTreeMap::new(),
|
||||
device: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -145,7 +156,8 @@ serde_impl!(Inode(u8?) {
|
|||
cum_size: u64 => 12,
|
||||
cum_dirs: usize => 13,
|
||||
cum_files: usize => 14,
|
||||
xattrs: BTreeMap<String, msgpack::Bytes> => 15
|
||||
xattrs: BTreeMap<String, msgpack::Bytes> => 15,
|
||||
device: Option<(u32, u32)> => 16
|
||||
});
|
||||
|
||||
|
||||
|
@ -165,12 +177,22 @@ impl Inode {
|
|||
FileType::Directory
|
||||
} else if meta.file_type().is_symlink() {
|
||||
FileType::Symlink
|
||||
} else if meta.file_type().is_block_device() {
|
||||
FileType::BlockDevice
|
||||
} else if meta.file_type().is_char_device() {
|
||||
FileType::CharDevice
|
||||
} else {
|
||||
return Err(InodeError::UnsupportedFiletype(path.to_owned()));
|
||||
};
|
||||
if meta.file_type().is_symlink() {
|
||||
inode.symlink_target = Some(try!(fs::read_link(path).map_err(|e| InodeError::ReadLinkTarget(e, path.to_owned()))).to_string_lossy().to_string());
|
||||
}
|
||||
if meta.file_type().is_block_device() || meta.file_type().is_char_device() {
|
||||
let rdev = meta.rdev();
|
||||
let major = (rdev >> 8) as u32;
|
||||
let minor = (rdev & 0xff) as u32;
|
||||
inode.device = Some((major, minor));
|
||||
}
|
||||
inode.mode = meta.permissions().mode();
|
||||
inode.user = meta.st_uid();
|
||||
inode.group = meta.st_gid();
|
||||
|
@ -202,6 +224,22 @@ impl Inode {
|
|||
} else {
|
||||
return Err(InodeError::Integrity("Symlink without target"))
|
||||
}
|
||||
},
|
||||
FileType::BlockDevice | FileType::CharDevice => {
|
||||
let name = try!(ffi::CString::new(full_path.as_os_str().as_bytes()).map_err(|_| InodeError::Integrity("Name contains nulls")));
|
||||
let mode = self.mode | match self.file_type {
|
||||
FileType::BlockDevice => libc::S_IFBLK,
|
||||
FileType::CharDevice => libc::S_IFCHR,
|
||||
_ => unreachable!()
|
||||
};
|
||||
let device = if let Some((major, minor)) = self.device {
|
||||
unsafe { libc::makedev(major, minor) }
|
||||
} else {
|
||||
return Err(InodeError::Integrity("Device without id"))
|
||||
};
|
||||
if unsafe { libc::mknod(name.as_ptr(), mode, device) } != 0 {
|
||||
return Err(InodeError::Create(io::Error::last_os_error(), full_path.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
let time = FileTime::from_seconds_since_1970(self.timestamp as u64, 0);
|
||||
|
|
|
@ -85,6 +85,8 @@ fn inode_from_entry<R: Read>(entry: &mut tar::Entry<R>) -> Result<Inode, Reposit
|
|||
tar::EntryType::Regular | tar::EntryType::Link | tar::EntryType::Continuous => FileType::File,
|
||||
tar::EntryType::Symlink => FileType::Symlink,
|
||||
tar::EntryType::Directory => FileType::Directory,
|
||||
tar::EntryType::Block => FileType::BlockDevice,
|
||||
tar::EntryType::Char => FileType::CharDevice,
|
||||
_ => return Err(InodeError::UnsupportedFiletype(path.to_path_buf()).into())
|
||||
};
|
||||
Inode {
|
||||
|
@ -96,6 +98,10 @@ fn inode_from_entry<R: Read>(entry: &mut tar::Entry<R>) -> Result<Inode, Reposit
|
|||
user: try!(header.uid()),
|
||||
group: try!(header.gid()),
|
||||
timestamp: try!(header.mtime()) as i64,
|
||||
device: match file_type {
|
||||
FileType::BlockDevice | FileType::CharDevice => Some((try!(header.device_major()).unwrap_or(0), try!(header.device_minor()).unwrap_or(0))),
|
||||
_ => None
|
||||
},
|
||||
..Default::default()
|
||||
}
|
||||
};
|
||||
|
@ -305,6 +311,10 @@ impl Repository {
|
|||
try!(header.set_link_name(target));
|
||||
}
|
||||
}
|
||||
if let Some((major, minor)) = inode.device {
|
||||
try!(header.set_device_major(major));
|
||||
try!(header.set_device_minor(minor));
|
||||
}
|
||||
header.set_mode(inode.mode);
|
||||
header.set_uid(inode.user);
|
||||
if let Some(name) = backup.user_names.get(&inode.user) {
|
||||
|
@ -318,7 +328,9 @@ impl Repository {
|
|||
header.set_entry_type(match inode.file_type {
|
||||
FileType::File => tar::EntryType::Regular,
|
||||
FileType::Symlink => tar::EntryType::Symlink,
|
||||
FileType::Directory => tar::EntryType::Directory
|
||||
FileType::Directory => tar::EntryType::Directory,
|
||||
FileType::BlockDevice => tar::EntryType::Block,
|
||||
FileType::CharDevice => tar::EntryType::Char
|
||||
});
|
||||
header.set_cksum();
|
||||
match inode.data {
|
||||
|
|
Loading…
Reference in New Issue