From 9fa9857a7514469d062335cd2dcb8c0d06eb624c Mon Sep 17 00:00:00 2001
From: "A. Wilcox" <AWilcox@Wilcox-Tech.com>
Date: Tue, 11 Feb 2020 13:41:10 -0600
Subject: Enter DiskMan

---
 diskman/CMakeLists.txt | 22 ++++++++++++
 diskman/disk.cc        | 67 +++++++++++++++++++++++++++++++++++
 diskman/disk.hh        | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++
 diskman/diskman.cc     | 81 ++++++++++++++++++++++++++++++++++++++++++
 diskman/diskman.hh     | 44 +++++++++++++++++++++++
 diskman/lvmhelp.cc     |  0
 diskman/lvmhelp.hh     |  0
 diskman/partition.cc   |  0
 diskman/partition.hh   |  0
 9 files changed, 310 insertions(+)
 create mode 100644 diskman/CMakeLists.txt
 create mode 100644 diskman/disk.cc
 create mode 100644 diskman/disk.hh
 create mode 100644 diskman/diskman.cc
 create mode 100644 diskman/diskman.hh
 create mode 100644 diskman/lvmhelp.cc
 create mode 100644 diskman/lvmhelp.hh
 create mode 100644 diskman/partition.cc
 create mode 100644 diskman/partition.hh

(limited to 'diskman')

diff --git a/diskman/CMakeLists.txt b/diskman/CMakeLists.txt
new file mode 100644
index 0000000..ea0c5fe
--- /dev/null
+++ b/diskman/CMakeLists.txt
@@ -0,0 +1,22 @@
+pkg_check_modules(FDISK REQUIRED fdisk)
+find_package(Boost REQUIRED)
+
+set(DISKMAN_SOURCE
+        diskman.cc
+        disk.cc
+        lvmhelp.cc
+        partition.cc
+)
+
+set(DISKMAN_INCLUDE
+        diskman.hh
+        disk.hh
+        partition.hh
+)
+
+include_directories(${Boost_INCLUDE_DIRS})
+add_library(diskman ${DISKMAN_SOURCE})
+target_link_libraries(diskman ${FDISK_LIBRARIES} ${LIBUDEV_LIBRARIES})
+
+install(TARGETS diskman DESTINATION lib)
+install(FILES ${DISKMAN_INCLUDE} DESTINATION include/diskman)
diff --git a/diskman/disk.cc b/diskman/disk.cc
new file mode 100644
index 0000000..ddb357f
--- /dev/null
+++ b/diskman/disk.cc
@@ -0,0 +1,67 @@
+/*
+ * disk.cc - Implementation of the Disk class
+ * diskman, the Disk Manipulation library for
+ * Project Horizon
+ *
+ * Copyright (c) 2020 Adélie Linux and contributors.  All rights reserved.
+ * This code is licensed under the AGPL 3.0 license, as noted in the
+ * LICENSE-code file in the root directory of this repository.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+#include "disk.hh"
+
+#include <cstring>
+#include <libudev.h>
+
+namespace Horizon {
+namespace DiskMan {
+
+/*! The full serial number of the disk */
+std::string _full_serial;
+
+#define SAFE_SET(ivar, udev_call) \
+    value = udev_call;\
+    if(value != nullptr) {\
+        ivar = std::string(value);\
+    }
+
+Disk::Disk(void *creation, bool partition) {
+    struct udev_device *device = static_cast<struct udev_device *>(creation);
+    const char *value;
+
+    SAFE_SET(_name, udev_device_get_sysname(device));
+    SAFE_SET(_model, udev_device_get_property_value(device, "ID_MODEL"));
+    SAFE_SET(_node, udev_device_get_devnode(device));
+    SAFE_SET(_devpath, udev_device_get_devpath(device));
+
+    value = udev_device_get_property_value(device, "ID_PART_TABLE_TYPE");
+    if(value == nullptr) {
+        _has_label = false;
+        _label = Unknown;
+    } else {
+        _has_label = true;
+        if(::strcmp(value, "apm") == 0) {
+            _label = APM;
+        } else if(::strcmp(value, "dos") == 0) {
+            _label = MBR;
+        } else if(::strcmp(value, "gpt") == 0) {
+            _label = GPT;
+        } else {
+            _label = Unknown;
+        }
+    }
+
+    value = udev_device_get_property_value(device, "ID_FS_TYPE");
+    if(value == nullptr) {
+        _has_fs = false;
+    } else {
+        _has_fs = true;
+        _fs_type = std::string(value);
+        SAFE_SET(_fs_label, udev_device_get_property_value(device, "ID_FS_LABEL"));
+    }
+}
+
+}
+}
diff --git a/diskman/disk.hh b/diskman/disk.hh
new file mode 100644
index 0000000..dcb9793
--- /dev/null
+++ b/diskman/disk.hh
@@ -0,0 +1,96 @@
+/*
+ * disk.hh - Public definition of the Disk class
+ * diskman, the Disk Manipulation library for
+ * Project Horizon
+ *
+ * Copyright (c) 2020 Adélie Linux and contributors.  All rights reserved.
+ * This code is licensed under the AGPL 3.0 license, as noted in the
+ * LICENSE-code file in the root directory of this repository.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+#ifndef DISKMAN__DISK_HH
+#define DISKMAN__DISK_HH
+
+#include <memory>
+#include <string>
+
+namespace Horizon {
+namespace DiskMan {
+
+/*! Represents a fixed disk device. */
+class Disk {
+public:
+    /*! Potential disk label types */
+    enum Label {
+        /*! GUID Partition Table, common on x86 and POWER */
+        GPT,
+        /*! Master Boot Record, common on x86 and ARM */
+        MBR,
+        /*! Apple Partition Map, common on POWER */
+        APM,
+        /*! Unknown disk label type */
+        Unknown
+    };
+
+    /*! Retrieve the disk's name.  For instance, "sda" or "nvme0n1". */
+    const std::string name() const { return this->_name; }
+    /*! Retrieve the disk's model.  This is specified by the vendor. */
+    const std::string model() const { return this->_model; }
+    /*! Retrieve the disk's serial number.  This may be equivalent to
+     * the model name, if the vendor did not specify a serial number. */
+    const std::string serial() const { return this->_full_serial; }
+    /*! Retrieve the device node for this disk.  For instance, "/dev/sda". */
+    const std::string node() const { return this->_node; }
+    /*! Retrieve the system device path for this disk. */
+    const std::string dev_path() const { return this->_devpath; }
+
+    /*! Determine if this disk has a disk label attached. */
+    bool has_label() const { return this->_has_label; }
+    /*! Retrieve the type of disk label in use on this disk.
+     *  Only valid if has_label() is true. */
+    enum Label label() const { return this->_label; }
+
+    /*! Determine if this disk has a file system written to it.
+     *  If this method returns true, the file system is directly written to
+     *  the disk; it is not a partition inside a disklabel. */
+    bool has_fs() const { return this->_has_fs; }
+    /*! Retrieve the type of file system written on this disk.
+     *  Only valid if has_fs() is true. */
+    const std::string fs_type() const { return this->_fs_type; }
+    /*! Retrieve the label of the file system written on this disk.
+     *  Only valid if has_fs() is true. */
+    const std::string fs_label() const { return this->_fs_label; }
+private:
+    /*! The name of the disk ("sda") */
+    std::string _name;
+    /*! The model name of the disk ("WDC WDBNCE2500PNC") */
+    std::string _model;
+    /*! The full serial number of the disk */
+    std::string _full_serial;
+    /*! The device node of the disk ("/dev/sda") */
+    std::string _node;
+    /*! The device path of the disk ("/sys/devices/...") */
+    std::string _devpath;
+
+    /*! Whether this disk has a disklabel */
+    bool _has_label;
+    /*! The type of disk label used, if +has_label+ is true */
+    enum Label _label;
+
+    /*! Whether this disk has a direct filesystem (non-labelled) */
+    bool _has_fs;
+    /*! The type of file system in use, if +has_fs+ is true */
+    std::string _fs_type;
+    /*! The label of the file system, if +has_fs+ is true */
+    std::string _fs_label;
+
+    Disk(void *creation, bool partition);
+    friend class DiskMan;
+};
+
+}
+}
+
+#endif  /* !DISKMAN__DISK_HH */
diff --git a/diskman/diskman.cc b/diskman/diskman.cc
new file mode 100644
index 0000000..32a2c40
--- /dev/null
+++ b/diskman/diskman.cc
@@ -0,0 +1,81 @@
+/*
+ * diskman.cc - Common routines for the DiskMan library
+ * diskman, the Disk Manipulation library for
+ * Project Horizon
+ *
+ * Copyright (c) 2020 Adélie Linux and contributors.  All rights reserved.
+ * This code is licensed under the AGPL 3.0 license, as noted in the
+ * LICENSE-code file in the root directory of this repository.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+#include "diskman.hh"
+
+#include <iostream>
+#include <libudev.h>
+
+namespace Horizon {
+namespace DiskMan {
+
+struct DiskMan::impl {
+    struct udev *udev;
+
+    impl() {
+        udev = udev_new();
+    }
+    ~impl() {
+        udev_unref(udev);
+    }
+    impl(const impl &other) {
+        this->udev = udev_ref(other.udev);
+    }
+};
+
+DiskMan::DiskMan() : pImpl {std::make_unique<impl>()} {
+}
+
+DiskMan::~DiskMan() = default;
+
+std::vector<Disk> DiskMan::find_disks(bool include_part, bool include_vg,
+                                      bool include_lvm) {
+    struct udev_enumerate *disk_enum = udev_enumerate_new(pImpl->udev);
+    struct udev_list_entry *first, *item;
+    struct udev_device *device = nullptr;
+    std::vector<Disk> disks;
+
+    if(disk_enum == nullptr) {
+        std::cerr << "Couldn't connect to udev" << std::endl;
+        return {};
+    }
+    udev_enumerate_add_match_subsystem(disk_enum, "block");
+    udev_enumerate_add_match_property(disk_enum, "DEVTYPE", "disk");
+    udev_enumerate_scan_devices(disk_enum);
+    first = udev_enumerate_get_list_entry(disk_enum);
+    if(first == NULL) {
+        std::cerr << "No block devices found" << std::endl;
+        return {};
+    }
+
+    udev_list_entry_foreach(item, first) {
+        const char *path = udev_list_entry_get_name(item);
+        if(device != NULL) udev_device_unref(device);
+        device = udev_device_new_from_syspath(pImpl->udev, path);
+        std::string name(udev_device_get_sysname(device));
+        if(name.compare(0, 4, "loop") == 0
+                || name.compare(0, 3, "ram") == 0) {
+            /* Don't include loop or ram devices */
+            continue;
+        }
+        if(!include_lvm && name.compare(0, 3, "dm-") == 0) {
+            /* Skip LVM volumes if requested. */
+            continue;
+        }
+        disks.push_back(Disk(device, include_part));
+    }
+
+    return disks;
+}
+
+}
+}
diff --git a/diskman/diskman.hh b/diskman/diskman.hh
new file mode 100644
index 0000000..ba9d4be
--- /dev/null
+++ b/diskman/diskman.hh
@@ -0,0 +1,44 @@
+/*
+ * diskman.hh - Public interface for the DiskMan library
+ * diskman, the Disk Manipulation library for
+ * Project Horizon
+ *
+ * Copyright (c) 2020 Adélie Linux and contributors.  All rights reserved.
+ * This code is licensed under the AGPL 3.0 license, as noted in the
+ * LICENSE-code file in the root directory of this repository.
+ *
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+#ifndef DISKMAN__DISKMAN_HH
+#define DISKMAN__DISKMAN_HH
+
+#include <memory>
+#include <vector>
+
+#include "disk.hh"
+
+namespace Horizon {
+namespace DiskMan {
+
+class DiskMan {
+    struct impl;
+    std::unique_ptr<impl> pImpl;
+public:
+    DiskMan();
+    ~DiskMan();
+
+    /*! Find all available disks on this system.
+     * @param include_part      Include partitions with disks.
+     * @param include_vg        Include LVM volume groups as disks.
+     * @param include_lvm       Include LVM volumes as disks.
+     */
+    std::vector<Disk> find_disks(bool include_part = true,
+                                 bool include_vg = true,
+                                 bool include_lvm = false);
+};
+
+}
+}
+
+#endif  /* !DISKMAN__DISKMAN_HH */
diff --git a/diskman/lvmhelp.cc b/diskman/lvmhelp.cc
new file mode 100644
index 0000000..e69de29
diff --git a/diskman/lvmhelp.hh b/diskman/lvmhelp.hh
new file mode 100644
index 0000000..e69de29
diff --git a/diskman/partition.cc b/diskman/partition.cc
new file mode 100644
index 0000000..e69de29
diff --git a/diskman/partition.hh b/diskman/partition.hh
new file mode 100644
index 0000000..e69de29
-- 
cgit v1.2.3-70-g09d2