Added support for block/char devices

This commit is contained in:
Dennis Schwerdel 2017-06-20 12:38:16 +02:00
parent 8d45176146
commit 9231800f3b
7 changed files with 76 additions and 11 deletions

View File

@ -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

View File

@ -1,6 +1,5 @@
# Mounted locations and pseudo filesystems
/cdrom
/dev
lost+found
/mnt
/sys

View File

@ -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:

View File

@ -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)
}
}
}

View File

@ -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
}
}

View File

@ -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);

View File

@ -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 {