root/sys/geom/label/g_label_swaplinux.c
/*-
 * SPDX-License-Identifier: BSD-2-Clause
 */

#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/malloc.h>

#include <geom/geom.h>
#include <geom/geom_dbg.h>
#include <geom/label/g_label.h>

/*
 * Taken from
 * https://github.com/util-linux/util-linux/blob/master/include/swapheader.h
 */

#define SWAP_VERSION 1
#define SWAP_UUID_LENGTH 16
#define SWAP_LABEL_LENGTH 16
#define SWAP_SIGNATURE "SWAPSPACE2"
#define SWAP_SIGNATURE_SZ (sizeof(SWAP_SIGNATURE) - 1)

struct swap_header_v1_2 {
        char          bootbits[1024];    /* Space for disklabel etc. */
        uint32_t      version;
        uint32_t      last_page;
        uint32_t      nr_badpages;
        unsigned char uuid[SWAP_UUID_LENGTH];
        char          volume_name[SWAP_LABEL_LENGTH];
        uint32_t      padding[117];
        uint32_t      badpages[1];
};

typedef union {
        struct swap_header_v1_2 header;
        struct {
                uint8_t reserved[PAGE_SIZE - SWAP_SIGNATURE_SZ];
                char    signature[SWAP_SIGNATURE_SZ];
        } tail;
} swhdr_t;

#define sw_version      header.version
#define sw_volume_name  header.volume_name
#define sw_signature    tail.signature

static void
g_label_swaplinux_taste(struct g_consumer *cp, char *label, size_t size)
{
        struct g_provider *pp;
        swhdr_t *hdr;

        g_topology_assert_not();
        pp = cp->provider;
        label[0] = '\0';

        KASSERT(pp->sectorsize != 0, ("Tasting a disk with 0 sectorsize"));
        if ((PAGE_SIZE % pp->sectorsize) != 0)
                return;

        hdr = (swhdr_t *)g_read_data(cp, 0, PAGE_SIZE, NULL);
        if (hdr == NULL)
                return;

        /* Check version and magic string */
        if (hdr->sw_version == SWAP_VERSION &&
            !memcmp(hdr->sw_signature, SWAP_SIGNATURE, SWAP_SIGNATURE_SZ))
                G_LABEL_DEBUG(1, "linux swap detected on %s.", pp->name);
        else
                goto exit_free;

        /* Check for volume label */
        if (hdr->sw_volume_name[0] == '\0')
                goto exit_free;

        /* Terminate label */
        hdr->sw_volume_name[sizeof(hdr->sw_volume_name) - 1] = '\0';
        strlcpy(label, hdr->sw_volume_name, size);

exit_free:
        g_free(hdr);
}

struct g_label_desc g_label_swaplinux = {
        .ld_taste = g_label_swaplinux_taste,
        .ld_dirprefix = "swaplinux/",
        .ld_enabled = 1
};

G_LABEL_INIT(swaplinux, g_label_swaplinux, "Create device nodes for Linux swap");