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
|
### UNRELEASED
|
||||||
* [added] Added `copy` subcommand
|
* [added] Added `copy` subcommand
|
||||||
* [added] Added support for xattrs in fuse mount
|
* [added] Added support for xattrs in fuse mount
|
||||||
|
* [added] Added support for block/char devices
|
||||||
* [modified] Also documenting common flags in subcommands
|
* [modified] Also documenting common flags in subcommands
|
||||||
* [modified] Using repository aliases (**conversion needed**)
|
* [modified] Using repository aliases (**conversion needed**)
|
||||||
* [modified] Remote path must be absolute
|
* [modified] Remote path must be absolute
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
# Mounted locations and pseudo filesystems
|
# Mounted locations and pseudo filesystems
|
||||||
/cdrom
|
/cdrom
|
||||||
/dev
|
|
||||||
lost+found
|
lost+found
|
||||||
/mnt
|
/mnt
|
||||||
/sys
|
/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
|
- `Directory` means a directory that does not contain data but might have
|
||||||
children
|
children
|
||||||
- `Symlink` means a symlink that points to a target
|
- `Symlink` means a symlink that points to a target
|
||||||
|
- `BlockDevice` means a block device
|
||||||
|
- `CharDevice` mean a character device
|
||||||
|
|
||||||
FileType {
|
FileType {
|
||||||
File => 0,
|
File => 0,
|
||||||
Directory => 1,
|
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
|
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
|
metadata). `cum_dirs` and `cum_files` is the count of directories and
|
||||||
non-directories (symlinks and regular files).
|
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 {
|
Inode {
|
||||||
name: string => 0,
|
name: string => 0,
|
||||||
|
@ -447,6 +454,8 @@ non-directories (symlinks and regular files).
|
||||||
cum_size: int => 12,
|
cum_size: int => 12,
|
||||||
cum_dirs: int => 13,
|
cum_dirs: int => 13,
|
||||||
cum_files: int => 14
|
cum_files: int => 14
|
||||||
|
xattrs: {string => bytes}? => 15,
|
||||||
|
device: (int, int)? => 16
|
||||||
}
|
}
|
||||||
|
|
||||||
This structure is encoded with the following field default values:
|
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::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::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::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::Directory => fuse::FileType::Directory,
|
||||||
FileType::File => fuse::FileType::RegularFile,
|
FileType::File => fuse::FileType::RegularFile,
|
||||||
FileType::Symlink => fuse::FileType::Symlink,
|
FileType::Symlink => fuse::FileType::Symlink,
|
||||||
|
FileType::BlockDevice => fuse::FileType::BlockDevice,
|
||||||
|
FileType::CharDevice => fuse::FileType::CharDevice
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,7 +114,7 @@ impl FuseInode {
|
||||||
nlink: 1,
|
nlink: 1,
|
||||||
uid: uid,
|
uid: uid,
|
||||||
gid: gid,
|
gid: gid,
|
||||||
rdev: 0,
|
rdev: self.inode.device.map_or(0, |(major, minor)| (major << 8) + minor),
|
||||||
flags: 0
|
flags: 0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,14 +2,17 @@ use ::prelude::*;
|
||||||
|
|
||||||
use filetime::{self, FileTime};
|
use filetime::{self, FileTime};
|
||||||
use xattr;
|
use xattr;
|
||||||
|
use libc;
|
||||||
|
|
||||||
use std::collections::BTreeMap;
|
use std::collections::BTreeMap;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
use std::fs::{self, File, Permissions};
|
use std::fs::{self, File, Permissions};
|
||||||
use std::os::linux::fs::MetadataExt;
|
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::io::{self, Read, Write};
|
||||||
|
use std::os::unix::ffi::OsStrExt;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
use std::ffi;
|
||||||
|
|
||||||
|
|
||||||
quick_error!{
|
quick_error!{
|
||||||
|
@ -63,19 +66,25 @@ quick_error!{
|
||||||
pub enum FileType {
|
pub enum FileType {
|
||||||
File,
|
File,
|
||||||
Directory,
|
Directory,
|
||||||
Symlink
|
Symlink,
|
||||||
|
BlockDevice,
|
||||||
|
CharDevice
|
||||||
}
|
}
|
||||||
serde_impl!(FileType(u8) {
|
serde_impl!(FileType(u8) {
|
||||||
File => 0,
|
File => 0,
|
||||||
Directory => 1,
|
Directory => 1,
|
||||||
Symlink => 2
|
Symlink => 2,
|
||||||
|
BlockDevice => 3,
|
||||||
|
CharDevice => 4
|
||||||
});
|
});
|
||||||
impl fmt::Display for FileType {
|
impl fmt::Display for FileType {
|
||||||
fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, format: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
||||||
match *self {
|
match *self {
|
||||||
FileType::File => write!(format, "file"),
|
FileType::File => write!(format, "file"),
|
||||||
FileType::Directory => write!(format, "directory"),
|
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_size: u64,
|
||||||
pub cum_dirs: usize,
|
pub cum_dirs: usize,
|
||||||
pub cum_files: 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 {
|
impl Default for Inode {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -127,7 +137,8 @@ impl Default for Inode {
|
||||||
cum_size: 0,
|
cum_size: 0,
|
||||||
cum_dirs: 0,
|
cum_dirs: 0,
|
||||||
cum_files: 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_size: u64 => 12,
|
||||||
cum_dirs: usize => 13,
|
cum_dirs: usize => 13,
|
||||||
cum_files: usize => 14,
|
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
|
FileType::Directory
|
||||||
} else if meta.file_type().is_symlink() {
|
} else if meta.file_type().is_symlink() {
|
||||||
FileType::Symlink
|
FileType::Symlink
|
||||||
|
} else if meta.file_type().is_block_device() {
|
||||||
|
FileType::BlockDevice
|
||||||
|
} else if meta.file_type().is_char_device() {
|
||||||
|
FileType::CharDevice
|
||||||
} else {
|
} else {
|
||||||
return Err(InodeError::UnsupportedFiletype(path.to_owned()));
|
return Err(InodeError::UnsupportedFiletype(path.to_owned()));
|
||||||
};
|
};
|
||||||
if meta.file_type().is_symlink() {
|
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());
|
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.mode = meta.permissions().mode();
|
||||||
inode.user = meta.st_uid();
|
inode.user = meta.st_uid();
|
||||||
inode.group = meta.st_gid();
|
inode.group = meta.st_gid();
|
||||||
|
@ -202,6 +224,22 @@ impl Inode {
|
||||||
} else {
|
} else {
|
||||||
return Err(InodeError::Integrity("Symlink without target"))
|
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);
|
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::Regular | tar::EntryType::Link | tar::EntryType::Continuous => FileType::File,
|
||||||
tar::EntryType::Symlink => FileType::Symlink,
|
tar::EntryType::Symlink => FileType::Symlink,
|
||||||
tar::EntryType::Directory => FileType::Directory,
|
tar::EntryType::Directory => FileType::Directory,
|
||||||
|
tar::EntryType::Block => FileType::BlockDevice,
|
||||||
|
tar::EntryType::Char => FileType::CharDevice,
|
||||||
_ => return Err(InodeError::UnsupportedFiletype(path.to_path_buf()).into())
|
_ => return Err(InodeError::UnsupportedFiletype(path.to_path_buf()).into())
|
||||||
};
|
};
|
||||||
Inode {
|
Inode {
|
||||||
|
@ -96,6 +98,10 @@ fn inode_from_entry<R: Read>(entry: &mut tar::Entry<R>) -> Result<Inode, Reposit
|
||||||
user: try!(header.uid()),
|
user: try!(header.uid()),
|
||||||
group: try!(header.gid()),
|
group: try!(header.gid()),
|
||||||
timestamp: try!(header.mtime()) as i64,
|
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()
|
..Default::default()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -305,6 +311,10 @@ impl Repository {
|
||||||
try!(header.set_link_name(target));
|
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_mode(inode.mode);
|
||||||
header.set_uid(inode.user);
|
header.set_uid(inode.user);
|
||||||
if let Some(name) = backup.user_names.get(&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 {
|
header.set_entry_type(match inode.file_type {
|
||||||
FileType::File => tar::EntryType::Regular,
|
FileType::File => tar::EntryType::Regular,
|
||||||
FileType::Symlink => tar::EntryType::Symlink,
|
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();
|
header.set_cksum();
|
||||||
match inode.data {
|
match inode.data {
|
||||||
|
|
Loading…
Reference in New Issue