diff options
-rw-r--r-- | src/fsentry.rs | 201 | ||||
-rw-r--r-- | src/generation.rs | 27 |
2 files changed, 145 insertions, 83 deletions
diff --git a/src/fsentry.rs b/src/fsentry.rs index a04a3de..276e3f9 100644 --- a/src/fsentry.rs +++ b/src/fsentry.rs @@ -70,76 +70,22 @@ pub enum FsEntryError { #[allow(clippy::len_without_is_empty)] impl FilesystemEntry { /// Create an `FsEntry` from a file's metadata. - pub(crate) fn new( - path: &Path, - kind: FilesystemKind, - uid: u32, - gid: u32, - len: u64, - mode: u32, - mtime: i64, - mtime_ns: i64, - atime: i64, - atime_ns: i64, - cache: &mut UsersCache, - ) -> Result<Self, FsEntryError> { - let symlink_target = if kind == FilesystemKind::Symlink { - debug!("reading symlink target for {:?}", path); - let target = - read_link(path).map_err(|err| FsEntryError::ReadLink(path.to_path_buf(), err))?; - Some(target) - } else { - None - }; - - let user: String = if let Some(user) = cache.get_user_by_uid(uid) { - user.name().to_string_lossy().to_string() - } else { - "".to_string() - }; - let group = if let Some(group) = cache.get_group_by_gid(gid) { - group.name().to_string_lossy().to_string() - } else { - "".to_string() - }; - - Ok(Self { - path: path.to_path_buf().into_os_string().into_vec(), - kind, - len, - mode, - mtime, - mtime_ns, - atime, - atime_ns, - symlink_target, - uid, - gid, - user, - group, - }) - } - - /// Create an `FsEntry` from a file's metadata. pub fn from_metadata( path: &Path, meta: &Metadata, cache: &mut UsersCache, ) -> Result<Self, FsEntryError> { let kind = FilesystemKind::from_file_type(meta.file_type()); - Self::new( - path, - kind, - meta.st_uid(), - meta.st_gid(), - meta.len(), - meta.st_mode(), - meta.st_mtime(), - meta.st_mtime_nsec(), - meta.st_atime(), - meta.st_atime_nsec(), - cache, - ) + Ok(EntryBuilder::new(kind) + .path(path.to_path_buf()) + .len(meta.len()) + .mode(meta.st_mode()) + .mtime(meta.st_mtime(), meta.st_mtime_nsec()) + .atime(meta.st_atime(), meta.st_atime_nsec()) + .user(meta.st_uid(), cache)? + .group(meta.st_uid(), cache)? + .symlink_target()? + .build()) } /// Return the kind of file the entry refers to. @@ -194,6 +140,133 @@ impl FilesystemEntry { } } +#[derive(Debug)] +pub(crate) struct EntryBuilder { + kind: FilesystemKind, + path: PathBuf, + len: u64, + + // 16 bits should be enough for a Unix mode_t. + // https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html + // However, it's 32 bits on Linux, so that's what we store. + mode: u32, + + // Linux can store file system time stamps in nanosecond + // resolution. We store them as two 64-bit integers. + mtime: i64, + mtime_ns: i64, + atime: i64, + atime_ns: i64, + + // The target of a symbolic link, if any. + symlink_target: Option<PathBuf>, + + // User and group owning the file. We store them as both the + // numeric id and the textual name corresponding to the numeric id + // at the time of the backup. + uid: u32, + gid: u32, + user: String, + group: String, +} + +impl EntryBuilder { + pub(crate) fn new(kind: FilesystemKind) -> Self { + Self { + kind, + path: PathBuf::new(), + len: 0, + mode: 0, + mtime: 0, + mtime_ns: 0, + atime: 0, + atime_ns: 0, + symlink_target: None, + uid: 0, + user: "".to_string(), + gid: 0, + group: "".to_string(), + } + } + + pub(crate) fn build(self) -> FilesystemEntry { + FilesystemEntry { + kind: self.kind, + path: self.path.into_os_string().into_vec(), + len: self.len, + mode: self.mode, + mtime: self.mtime, + mtime_ns: self.mtime_ns, + atime: self.atime, + atime_ns: self.atime_ns, + symlink_target: self.symlink_target, + uid: self.uid, + user: self.user, + gid: self.gid, + group: self.group, + } + } + + pub(crate) fn path(mut self, path: PathBuf) -> Self { + self.path = path; + self + } + + pub(crate) fn len(mut self, len: u64) -> Self { + self.len = len; + self + } + + pub(crate) fn mode(mut self, mode: u32) -> Self { + self.mode = mode; + self + } + + pub(crate) fn mtime(mut self, secs: i64, nsec: i64) -> Self { + self.mtime = secs; + self.mtime_ns = nsec; + self + } + + pub(crate) fn atime(mut self, secs: i64, nsec: i64) -> Self { + self.atime = secs; + self.atime_ns = nsec; + self + } + + pub(crate) fn symlink_target(mut self) -> Result<Self, FsEntryError> { + self.symlink_target = if self.kind == FilesystemKind::Symlink { + debug!("reading symlink target for {:?}", self.path); + let target = read_link(&self.path) + .map_err(|err| FsEntryError::ReadLink(self.path.clone(), err))?; + Some(target) + } else { + None + }; + Ok(self) + } + + pub(crate) fn user(mut self, uid: u32, cache: &mut UsersCache) -> Result<Self, FsEntryError> { + self.uid = uid; + self.user = if let Some(user) = cache.get_user_by_uid(uid) { + user.name().to_string_lossy().to_string() + } else { + "".to_string() + }; + Ok(self) + } + + pub(crate) fn group(mut self, gid: u32, cache: &mut UsersCache) -> Result<Self, FsEntryError> { + self.gid = gid; + self.group = if let Some(group) = cache.get_group_by_gid(gid) { + group.name().to_string_lossy().to_string() + } else { + "".to_string() + }; + Ok(self) + } +} + /// Different types of file system entries. #[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)] pub enum FilesystemKind { diff --git a/src/generation.rs b/src/generation.rs index cec3d14..477edc0 100644 --- a/src/generation.rs +++ b/src/generation.rs @@ -297,33 +297,22 @@ impl LocalGeneration { #[cfg(test)] mod test { use super::{LabelChecksumKind, LocalGeneration, NascentGeneration, Reason, SchemaVersion}; - use crate::fsentry::FilesystemEntry; + use crate::fsentry::EntryBuilder; use crate::fsentry::FilesystemKind; - use std::path::Path; + use std::path::PathBuf; use tempfile::{tempdir, NamedTempFile}; - use users::UsersCache; #[test] fn round_trips_u64_max() { let tmp = tempdir().unwrap(); let filename = tmp.path().join("test.db"); - let mut cache = UsersCache::new(); + let path = PathBuf::from("/"); let schema = SchemaVersion::new(0, 0); { - let e = FilesystemEntry::new( - Path::new("/"), - FilesystemKind::Directory, - 0, - 0, - u64::MAX, - 0, - 0, - 0, - 0, - 0, - &mut cache, - ) - .unwrap(); + let e = EntryBuilder::new(FilesystemKind::Directory) + .path(path.clone()) + .len(u64::MAX) + .build(); let mut gen = NascentGeneration::create(&filename, schema, LabelChecksumKind::Sha256).unwrap(); gen.insert(e, &[], Reason::IsNew, false).unwrap(); @@ -331,7 +320,7 @@ mod test { } let db = LocalGeneration::open(&filename).unwrap(); - let e = db.get_file(Path::new("/")).unwrap().unwrap(); + let e = db.get_file(&path).unwrap().unwrap(); assert_eq!(e.len(), u64::MAX); } |