Most of the modern linux systems will manage the filesystem parititions using the device block UUID, a 128bit number represented as 32 hexdecimal digits. One can use command blkid to print out all the block device attributes, including the UUID. However, there are moments we would like to find that information programatically.

This is an exercise trying to call blkid low level functions through CGO, to figure out the block device UUID.

Photo by Mr Cup / Fabien Barral on Unsplash Photo by Mr Cup / Fabien Barral on Unsplash

Before we proceed to the golang implemnetation, let’s see how it is done in C as follows:

    blkid_probe pr;
    const char *uuid;
    
    pr = blkid_new_probe_from_filename(devname);
    if (!pr) {
      err(2, "Failed to open %s", devname);
    }
    
    blkid_do_probe(pr);
    blkid_probe_lookup_value(pr, "UUID", &uuid, NULL);
    
    printf("UUID=%s\n", uuid);
    
    blkid_free_probe(pr);

First, we create a new blkid probe based on the device name. Then we run the probe. Afterwards, we look up the UUID attributes from the probe result. Finally, we release resources allocated for this probe.

This is the golang implementation in CGO:

package blkid

/*
#include <unistd.h>
#include <sys/types.h>
#include <blkid/blkid.h>

#cgo CFLAGS: -O2
#cgo LDFLAGS: -lblkid  -luuid
*/
import "C"
import "fmt"

func GetUUIDForBlockDevice(devname string) (uuid string, err error) {
	pr := C.blkid_new_probe_from_filename(C.CString(devname))
	if pr == nil {
		err = fmt.Errorf("error to open device '%s'", devname)
		return
	}

	C.blkid_do_probe(pr)

	var value *C.char
	var ssize *C.size_t = nil

	if C.blkid_probe_lookup_value(pr, C.CString("UUID"), &value, ssize) == 0 {
		uuid = C.GoString(value)
	}

	C.blkid_free_probe(pr);

	return
}