summaryrefslogtreecommitdiffstats
path: root/discover/device-handler.c
diff options
context:
space:
mode:
Diffstat (limited to 'discover/device-handler.c')
-rw-r--r--discover/device-handler.c213
1 files changed, 186 insertions, 27 deletions
diff --git a/discover/device-handler.c b/discover/device-handler.c
index 64095f1..4e25e07 100644
--- a/discover/device-handler.c
+++ b/discover/device-handler.c
@@ -26,6 +26,7 @@
#include "device-handler.h"
#include "discover-server.h"
+#include "devmapper.h"
#include "user-event.h"
#include "platform.h"
#include "event.h"
@@ -56,6 +57,9 @@ struct device_handler {
struct discover_device **devices;
unsigned int n_devices;
+ struct ramdisk_device **ramdisks;
+ unsigned int n_ramdisks;
+
struct waitset *waitset;
struct waiter *timeout_waiter;
bool autoboot_enabled;
@@ -319,6 +323,7 @@ struct device_handler *device_handler_init(struct discover_server *server,
void device_handler_reinit(struct device_handler *handler)
{
struct discover_boot_option *opt, *tmp;
+ struct ramdisk_device *ramdisk;
unsigned int i;
device_handler_cancel_default(handler);
@@ -330,13 +335,20 @@ void device_handler_reinit(struct device_handler *handler)
list_init(&handler->unresolved_boot_options);
/* drop all devices */
- for (i = 0; i < handler->n_devices; i++)
+ for (i = 0; i < handler->n_devices; i++) {
discover_server_notify_device_remove(handler->server,
handler->devices[i]->device);
+ ramdisk = handler->devices[i]->ramdisk;
+ talloc_free(handler->devices[i]);
+ talloc_free(ramdisk);
+ }
talloc_free(handler->devices);
handler->devices = NULL;
handler->n_devices = 0;
+ talloc_free(handler->ramdisks);
+ handler->ramdisks = NULL;
+ handler->n_ramdisks = 0;
device_handler_reinit_sources(handler);
}
@@ -445,6 +457,7 @@ struct {
} device_type_map[] = {
{ IPMI_BOOTDEV_NETWORK, DEVICE_TYPE_NETWORK },
{ IPMI_BOOTDEV_DISK, DEVICE_TYPE_DISK },
+ { IPMI_BOOTDEV_DISK, DEVICE_TYPE_USB },
{ IPMI_BOOTDEV_CDROM, DEVICE_TYPE_OPTICAL },
};
@@ -748,6 +761,95 @@ void device_handler_add_device(struct device_handler *handler,
network_register_device(handler->network, device);
}
+void device_handler_add_ramdisk(struct device_handler *handler,
+ const char *path)
+{
+ struct ramdisk_device *dev;
+ unsigned int i;
+
+ if (!path)
+ return;
+
+ for (i = 0; i < handler->n_ramdisks; i++)
+ if (!strcmp(handler->ramdisks[i]->path, path))
+ return;
+
+ dev = talloc_zero(handler, struct ramdisk_device);
+ if (!dev) {
+ pb_log("Failed to allocate memory to track %s\n", path);
+ return;
+ }
+
+ dev->path = talloc_strdup(handler, path);
+
+ handler->ramdisks = talloc_realloc(handler, handler->ramdisks,
+ struct ramdisk_device *,
+ handler->n_ramdisks + 1);
+ if (!handler->ramdisks) {
+ pb_log("Failed to reallocate memory"
+ "- ramdisk tracking inconsistent!\n");
+ return;
+ }
+
+ handler->ramdisks[i] = dev;
+ i = handler->n_ramdisks++;
+}
+
+struct ramdisk_device *device_handler_get_ramdisk(
+ struct device_handler *handler)
+{
+ unsigned int i;
+ char *name;
+ dev_t id;
+
+ /* Check if free ramdisk exists */
+ for (i = 0; i < handler->n_ramdisks; i++)
+ if (!handler->ramdisks[i]->snapshot &&
+ !handler->ramdisks[i]->origin &&
+ !handler->ramdisks[i]->base)
+ return handler->ramdisks[i];
+
+ /* Otherwise create a new one */
+ name = talloc_asprintf(handler, "/dev/ram%d",
+ handler->n_ramdisks);
+ if (!name) {
+ pb_debug("Failed to allocate memory to name /dev/ram%d",
+ handler->n_ramdisks);
+ return NULL;
+ }
+
+ id = makedev(1, handler->n_ramdisks);
+ if (mknod(name, S_IFBLK, id)) {
+ if (errno == EEXIST) {
+ /* We haven't yet received updates for existing
+ * ramdisks - add and use this one */
+ pb_debug("Using untracked ramdisk %s\n", name);
+ } else {
+ pb_log("Failed to create new ramdisk %s: %s\n",
+ name, strerror(errno));
+ return NULL;
+ }
+ }
+ device_handler_add_ramdisk(handler, name);
+ talloc_free(name);
+
+ return handler->ramdisks[i];
+}
+
+void device_handler_release_ramdisk(struct discover_device *device)
+{
+ struct ramdisk_device *ramdisk = device->ramdisk;
+
+ talloc_free(ramdisk->snapshot);
+ talloc_free(ramdisk->origin);
+ talloc_free(ramdisk->base);
+
+ ramdisk->snapshot = ramdisk->origin = ramdisk->base = NULL;
+ ramdisk->sectors = 0;
+
+ device->ramdisk = NULL;
+}
+
/* Start discovery on a hotplugged device. The device will be in our devices
* array, but has only just been initialised by the hotplug source.
*/
@@ -1123,28 +1225,46 @@ static void device_handler_reinit_sources(struct device_handler *handler)
handler->dry_run);
}
-static const char *fs_parameters(unsigned int rw_flags, const char *fstype)
+static const char *fs_parameters(struct discover_device *dev,
+ unsigned int rw_flags)
{
+ const char *fstype = discover_device_get_param(dev, "ID_FS_TYPE");
+
+ /* XFS journals are not cross-endian compatible; don't try recovery
+ * even if we have a snapshot */
+ if (!strncmp(fstype, "xfs", strlen("xfs")))
+ return "norecovery";
+
+ /* If we have a snapshot available allow touching the filesystem */
+ if (dev->ramdisk)
+ return "";
+
if ((rw_flags | MS_RDONLY) != MS_RDONLY)
return "";
- /* Avoid writing back to the disk on journaled filesystems */
+ /* Avoid writes due to journal replay if we don't have a snapshot */
if (!strncmp(fstype, "ext4", strlen("ext4")))
return "norecovery";
- if (!strncmp(fstype, "xfs", strlen("xfs")))
- return "norecovery";
return "";
}
+static inline const char *get_device_path(struct discover_device *dev)
+{
+ return dev->ramdisk ? dev->ramdisk->snapshot : dev->device_path;
+}
+
static bool check_existing_mount(struct discover_device *dev)
{
struct stat devstat, mntstat;
+ const char *device_path;
struct mntent *mnt;
FILE *fp;
int rc;
- rc = stat(dev->device_path, &devstat);
+ device_path = get_device_path(dev);
+
+ rc = stat(device_path, &devstat);
if (rc) {
pb_debug("%s: stat failed: %s\n", __func__, strerror(errno));
return false;
@@ -1194,7 +1314,7 @@ static bool check_existing_mount(struct discover_device *dev)
static int mount_device(struct discover_device *dev)
{
- const char *fstype;
+ const char *fstype, *device_path;
int rc;
if (!dev->device_path)
@@ -1226,11 +1346,13 @@ static int mount_device(struct discover_device *dev)
goto err_free;
}
+ device_path = get_device_path(dev);
+
pb_log("mounting device %s read-only\n", dev->device_path);
errno = 0;
- rc = mount(dev->device_path, dev->mount_path, fstype,
+ rc = mount(device_path, dev->mount_path, fstype,
MS_RDONLY | MS_SILENT,
- fs_parameters(MS_RDONLY, fstype));
+ fs_parameters(dev, MS_RDONLY));
if (!rc) {
dev->mounted = true;
dev->mounted_rw = false;
@@ -1239,7 +1361,10 @@ static int mount_device(struct discover_device *dev)
}
pb_log("couldn't mount device %s: mount failed: %s\n",
- dev->device_path, strerror(errno));
+ device_path, strerror(errno));
+
+ /* If mount fails clean up any snapshot */
+ devmapper_destroy_snapshot(dev);
pb_rmdir_recursive(mount_base(), dev->mount_path);
err_free:
@@ -1250,17 +1375,21 @@ err_free:
static int umount_device(struct discover_device *dev)
{
+ const char *device_path;
int rc;
if (!dev->mounted || !dev->unmount)
return 0;
- pb_log("unmounting device %s\n", dev->device_path);
+ device_path = get_device_path(dev);
+
+ pb_log("unmounting device %s\n", device_path);
rc = umount(dev->mount_path);
if (rc)
return -1;
dev->mounted = false;
+ devmapper_destroy_snapshot(dev);
pb_rmdir_recursive(mount_base(), dev->mount_path);
@@ -1272,11 +1401,16 @@ static int umount_device(struct discover_device *dev)
int device_request_write(struct discover_device *dev, bool *release)
{
- const char *fstype;
+ const char *fstype, *device_path;
+ const struct config *config;
int rc;
*release = false;
+ config = config_get();
+ if (!config->allow_writes)
+ return -1;
+
if (!dev->mounted)
return -1;
@@ -1285,16 +1419,20 @@ int device_request_write(struct discover_device *dev, bool *release)
fstype = discover_device_get_param(dev, "ID_FS_TYPE");
- pb_log("remounting device %s read-write\n", dev->device_path);
+ device_path = get_device_path(dev);
+
+ pb_log("remounting device %s read-write\n", device_path);
rc = umount(dev->mount_path);
if (rc) {
- pb_log("Failed to unmount %s\n", dev->mount_path);
+ pb_log("Failed to unmount %s: %s\n",
+ dev->mount_path, strerror(errno));
return -1;
}
- rc = mount(dev->device_path, dev->mount_path, fstype,
+
+ rc = mount(device_path, dev->mount_path, fstype,
MS_SILENT,
- fs_parameters(MS_REMOUNT, fstype));
+ fs_parameters(dev, MS_REMOUNT));
if (rc)
goto mount_ro;
@@ -1303,29 +1441,50 @@ int device_request_write(struct discover_device *dev, bool *release)
return 0;
mount_ro:
- pb_log("Unable to remount device %s read-write\n", dev->device_path);
- rc = mount(dev->device_path, dev->mount_path, fstype,
+ pb_log("Unable to remount device %s read-write: %s\n",
+ device_path, strerror(errno));
+ if (mount(device_path, dev->mount_path, fstype,
MS_RDONLY | MS_SILENT,
- fs_parameters(MS_RDONLY, fstype));
- if (rc)
- pb_log("Unable to recover mount for %s\n", dev->device_path);
+ fs_parameters(dev, MS_RDONLY)))
+ pb_log("Unable to recover mount for %s: %s\n",
+ device_path, strerror(errno));
return -1;
}
void device_release_write(struct discover_device *dev, bool release)
{
- const char *fstype;
+ const char *fstype, *device_path;
+ int rc = 0;
if (!release)
return;
+ device_path = get_device_path(dev);
+
fstype = discover_device_get_param(dev, "ID_FS_TYPE");
- pb_log("remounting device %s read-only\n", dev->device_path);
- mount(dev->device_path, dev->mount_path, "",
- MS_REMOUNT | MS_RDONLY | MS_SILENT,
- fs_parameters(MS_RDONLY, fstype));
- dev->mounted_rw = false;
+ pb_log("remounting device %s read-only\n", device_path);
+
+ if (umount(dev->mount_path)) {
+ pb_log("Failed to unmount %s\n", dev->mount_path);
+ return;
+ }
+ dev->mounted_rw = dev->mounted = false;
+
+ if (dev->ramdisk) {
+ devmapper_merge_snapshot(dev);
+ /* device_path becomes stale after merge */
+ device_path = get_device_path(dev);
+ }
+
+ mount(device_path, dev->mount_path, fstype,
+ MS_RDONLY | MS_SILENT,
+ fs_parameters(dev, MS_RDONLY));
+ if (rc)
+ pb_log("Failed to remount %s read-only: %s\n",
+ device_path, strerror(errno));
+ else
+ dev->mounted = true;
}
#else
OpenPOWER on IntegriCloud