<NME> lib.rs
<BEF> #[cfg(feature = "min")]
#[cfg_attr(feature = "dbus_enabled", allow(dead_code))]
#[macro_use]
extern crate nix;

#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate log;

#[cfg(test)]
extern crate uuid;
extern crate chrono;
extern crate dbus;
extern crate term;
extern crate rand;
extern crate serde;
#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate serde_json;

#[macro_use]
extern crate libcryptsetup_rs;

#[macro_use]
mod macros;

pub mod engine;

#[cfg(feature = "dbus_enabled")]
pub mod dbus_api;

pub mod stratis;

#[cfg(feature = "min")]
pub mod jsonrpc;

#[cfg(feature = "systemd_compat")]
pub mod systemd;

<MSG> Add snapshot support to filesystem

A snapshot uses the DM create_snap feature.  See "Internal snapshots" in:

https://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt

Once created a snapshot of a filesytem is no different than any other
thin-provisioned device.

filesystem.rs deals with updating the XFS UUID to avoid any duplicate
UUIDs.

Signed-off-by: Todd Gill <7afbb5ba98932b8ffb9cc9e6675c4792db142529@redhat.com>

<DFF> @@ -12,6 +12,7 @@ extern crate byteorder;
 extern crate uuid;
 extern crate chrono;
 extern crate dbus;
+extern crate tempdir;
 extern crate term;
 extern crate rand;
 extern crate serde;

{'commit': 'diff --git a/Cargo.toml b/Cargo.toml\nindex b5c791a3..9cb40731 100644\n--- a/Cargo.toml\n+++ b/Cargo.toml\n@@ -18,6 +18,7 @@ rand = "0.3.10"\n serde = "1"\n serde_derive = "1"\n serde_json = "1"\n+tempdir = "0.3.5"\n log = "0.3"\n env_logger="0.3.5"\n libc = "0.2"\n@@ -29,5 +30,4 @@ features = ["serde", "v4"]\n \n [dev-dependencies]\n quickcheck = "0.4"\n-tempdir = "0.3.5"\n loopdev = "0.1.1"\ndiff --git a/src/engine/strat_engine/filesystem.rs b/src/engine/strat_engine/filesystem.rs\nindex b75bd670..78597651 100644\n--- a/src/engine/strat_engine/filesystem.rs\n+++ b/src/engine/strat_engine/filesystem.rs\n@@ -5,11 +5,13 @@\n use std::path::{Path, PathBuf};\n use std::process::Command;\n \n-use devicemapper::{Bytes, DmDevice, DM, IEC, SECTOR_SIZE, Sectors, ThinDev, ThinDevId, ThinStatus,\n-                   ThinPoolDev};\n+use devicemapper::{Bytes, DmDevice, DM, DmName, IEC, SECTOR_SIZE, Sectors, ThinDev, ThinDevId,\n+                   ThinStatus, ThinPoolDev};\n \n use nix::sys::statvfs::statvfs;\n use nix::sys::statvfs::vfs::Statvfs;\n+use nix::mount::{mount, MsFlags, umount};\n+use tempdir::TempDir;\n \n use super::super::engine::{Filesystem, HasName, HasUuid};\n use super::super::errors::{EngineError, EngineResult, ErrorEnum};\n@@ -56,6 +58,52 @@ impl StratFilesystem {\n         }\n     }\n \n+    /// Create a snapshot of the ThinDev under the filesystem. Return the\n+    /// resulting ThinDev to the caller.\n+    pub fn snapshot(&self,\n+                    dm: &DM,\n+                    thin_pool: &ThinPoolDev,\n+                    snapshot_name: &DmName,\n+                    snapshot_fs_uuid: FilesystemUuid,\n+                    snapshot_thin_id: ThinDevId)\n+                    -> EngineResult<StratFilesystem> {\n+\n+        match self.thin_dev\n+                  .snapshot(dm, thin_pool, snapshot_name, snapshot_thin_id) {\n+            Ok(thin_dev) => {\n+                // If the source is mounted, XFS puts a dummy record in the\n+                // log to enforce replay of the snapshot to deal with any\n+                // orphaned inodes. The dummy record put the log in a dirty\n+                // state. xfs_admin won\'t allow a filesystem UUID\n+                // to be updated when the log is dirty.  To clear the log\n+                // we mount/unmount the filesystem before updating the UUID.\n+                //\n+                // If the source is unmounted the XFS log will be clean so\n+                // we can skip the mount/unmount.\n+                if self.get_mount_point()?.is_some() {\n+                    let tmp_dir = TempDir::new("stratis_mp_")?;\n+                    // Mount the snapshot with the "nouuid" option. mount\n+                    // will fail due to duplicate UUID otherwise.\n+                    mount(Some(&thin_dev.devnode()),\n+                          tmp_dir.path(),\n+                          Some("xfs"),\n+                          MsFlags::empty(),\n+                          Some("nouuid"))?;\n+                    umount(tmp_dir.path())?;\n+                }\n+                set_uuid(&thin_dev.devnode(), snapshot_fs_uuid)?;\n+                Ok(StratFilesystem::setup(snapshot_fs_uuid, snapshot_name.as_ref(), thin_dev))\n+            }\n+            Err(e) => {\n+                Err(EngineError::Engine(ErrorEnum::Error,\n+                                        format!("failed to create {} snapshot for {} - {}",\n+                                                snapshot_name,\n+                                                self.name,\n+                                                e)))\n+            }\n+        }\n+    }\n+\n     /// check if filesystem is getting full and needs to be extended\n     /// TODO: deal with the thindev in a Fail state.\n     pub fn check(&mut self, dm: &DM) -> EngineResult<FilesystemStatus> {\n@@ -193,3 +241,18 @@ pub fn create_fs(devnode: &Path, uuid: &FilesystemUuid) -> EngineResult<()> {\n         Err(EngineError::Engine(ErrorEnum::Error, err_msg))\n     }\n }\n+\n+/// Generate a new UUID filesystem on the devnode.\n+pub fn set_uuid(devnode: &Path, snapshot_fs_uuid: FilesystemUuid) -> EngineResult<()> {\n+    if Command::new("xfs_admin")\n+           .arg("-U")\n+           .arg(format!("{}", snapshot_fs_uuid))\n+           .arg(&devnode)\n+           .status()?\n+           .success() {\n+        Ok(())\n+    } else {\n+        let err_msg = format!("Failed to generate UUID for filesystem at {:?}", devnode);\n+        Err(EngineError::Engine(ErrorEnum::Error, err_msg))\n+    }\n+}\ndiff --git a/src/engine/strat_engine/pool.rs b/src/engine/strat_engine/pool.rs\nindex 5fc51b94..3a7a04d9 100644\n--- a/src/engine/strat_engine/pool.rs\n+++ b/src/engine/strat_engine/pool.rs\n@@ -129,6 +129,12 @@ impl StratPool {\n     pub fn has_filesystems(&self) -> bool {\n         self.thin_pool.has_filesystems()\n     }\n+\n+    pub fn snapshot_filesystem(&mut self, filesystem_uuid: Uuid) -> EngineResult<FilesystemUuid> {\n+        let dm = DM::new()?;\n+        self.thin_pool\n+            .snapshot_filesystem(&dm, self.pool_uuid, filesystem_uuid)\n+    }\n }\n \n impl Pool for StratPool {\ndiff --git a/src/engine/strat_engine/thinpool.rs b/src/engine/strat_engine/thinpool.rs\nindex 5088b7a4..0bded1aa 100644\n--- a/src/engine/strat_engine/thinpool.rs\n+++ b/src/engine/strat_engine/thinpool.rs\n@@ -22,7 +22,7 @@ use super::blockdevmgr::{BlockDevMgr, BlkDevSegment, map_to_dm};\n use super::device::wipe_sectors;\n use super::dmdevice::{FlexRole, ThinDevIdPool, ThinPoolRole, ThinRole, format_flex_name,\n                       format_thinpool_name, format_thin_name};\n-use super::filesystem::{StratFilesystem, FilesystemStatus};\n+use super::filesystem::{FilesystemStatus, StratFilesystem};\n use super::mdv::MetadataVol;\n use super::serde_structs::{FilesystemSave, FlexDevsSave, Recordable, ThinPoolDevSave};\n \n@@ -479,6 +479,35 @@ impl ThinPool {\n         Ok(fs_uuid)\n     }\n \n+\n+    pub fn snapshot_filesystem(&mut self,\n+                               dm: &DM,\n+                               pool_uuid: PoolUuid,\n+                               filesystem_uuid: FilesystemUuid)\n+                               -> EngineResult<FilesystemUuid> {\n+        let snapshot_fs_uuid = Uuid::new_v4();\n+        let snapshot_name = format_thin_name(&pool_uuid, ThinRole::Filesystem(snapshot_fs_uuid));\n+        let snapshot_id = self.id_gen.new_id()?;\n+        let new_filesystem = match self.get_filesystem_by_uuid(&filesystem_uuid) {\n+            Some(filesystem) => {\n+                filesystem\n+                    .snapshot(dm,\n+                              &self.thin_pool,\n+                              snapshot_name.as_ref(),\n+                              snapshot_fs_uuid,\n+                              snapshot_id)?\n+            }\n+            None => {\n+                return Err(EngineError::Engine(ErrorEnum::Error,\n+                                               "snapshot_filesystem failed, filesystem not found"\n+                                                   .into()));\n+            }\n+        };\n+        self.mdv.save_fs(&new_filesystem)?;\n+        self.filesystems.insert(new_filesystem);\n+        Ok(snapshot_fs_uuid)\n+    }\n+\n     /// Destroy a filesystem within the thin pool.\n     pub fn destroy_filesystem(&mut self, dm: &DM, uuid: &FilesystemUuid) -> EngineResult<()> {\n         if let Some(fs) = self.filesystems.remove_by_uuid(uuid) {\ndiff --git a/src/lib.rs b/src/lib.rs\nindex 2e2a7aa0..50b1c216 100644\n--- a/src/lib.rs\n+++ b/src/lib.rs\n@@ -12,6 +12,7 @@ extern crate byteorder;\n extern crate uuid;\n extern crate chrono;\n extern crate dbus;\n+extern crate tempdir;\n extern crate term;\n extern crate rand;\n extern crate serde;\n', 'commit_subject': 'Add snapshot support to filesystem', 'repo_name': 'stratis-storage/stratisd', 'committer': {'name': 'Todd Gill', 'email': '7afbb5ba98932b8ffb9cc9e6675c4792db142529@redhat.com', 'time_sec': '1507843068', 'tz_offset': '-240', 'date': {'seconds': '1507843068'}}, 'commit_tree': 'cf14764eb34e3558f6ab1ef848458bba0e0163dd', 'commit_message': 'Add snapshot support to filesystem\n\nA snapshot uses the DM create_snap feature.  See "Internal snapshots" in:\n\nhttps://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt\n\nOnce created a snapshot of a filesytem is no different than any other\nthin-provisioned device.\n\nfilesystem.rs deals with updating the XFS UUID to avoid any duplicate\nUUIDs.\n\nSigned-off-by: Todd Gill <7afbb5ba98932b8ffb9cc9e6675c4792db142529@redhat.com>\n', 'language_name': 'Rust', 'license': 'mpl-2.0', 'hunks': '@@ -12,6 +12,7 @@ extern crate byteorder;\n extern crate uuid;\n extern crate chrono;\n extern crate dbus;\n+extern crate tempdir;\n extern crate term;\n extern crate rand;\n extern crate serde;\n', 'addition_count': 1, 'deletion_count': 0, 'src_file': 'a/src/lib.rs', 'tgt_file': 'b/src/lib.rs', 'file_extension': '.rs', 'before_file': '#[cfg(feature = "min")]\n#[cfg_attr(feature = "dbus_enabled", allow(dead_code))]\n#[macro_use]\nextern crate nix;\n\n#[macro_use]\nextern crate serde_derive;\n#[macro_use]\nextern crate log;\n\n#[cfg(test)]\nextern crate uuid;\nextern crate chrono;\nextern crate dbus;\nextern crate term;\nextern crate rand;\nextern crate serde;\n#[macro_use]\nextern crate lazy_static;\n\n#[macro_use]\nextern crate serde_json;\n\n#[macro_use]\nextern crate libcryptsetup_rs;\n\n#[macro_use]\nmod macros;\n\npub mod engine;\n\n#[cfg(feature = "dbus_enabled")]\npub mod dbus_api;\n\npub mod stratis;\n\n#[cfg(feature = "min")]\npub mod jsonrpc;\n\n#[cfg(feature = "systemd_compat")]\npub mod systemd;\n', 'message': 'Add snapshot support to filesystem\n\nA snapshot uses the DM create_snap feature.  See "Internal snapshots" in:\n\nhttps://www.kernel.org/doc/Documentation/device-mapper/thin-provisioning.txt\n\nOnce created a snapshot of a filesytem is no different than any other\nthin-provisioned device.\n\nfilesystem.rs deals with updating the XFS UUID to avoid any duplicate\nUUIDs.\n\nSigned-off-by: Todd Gill <7afbb5ba98932b8ffb9cc9e6675c4792db142529@redhat.com>\n', 'author_name': 'Todd Gill', 'lang': 'rs'}