How Get Uuid of a Device/Partition in Linux Programatically

Linux Programmatically find UUID's of Partitions

First of all, are you aware that there are UUIDs for both partitions and for filesystems? Neither is always present. Linux itself does not know about UUIDs, the entire concept is handled in user-space by programs installed on your system, and these programs all use libblkid, as far as I know.

You say that you want partition UUIDs, but you probably actually want filesystem UUIDs.

Libblkid

Sorry, but when you say that "libblkid isn't available", what does that even mean? I am a bit skeptical. Libblkid is open source and free software, after all, so if you don't have a copy of libblkid it is trivial to get one. Libblkid is also written in C, and you're already writing code in C, so you must have a C compiler around to compile libblkid. You can make a static version of it if necessary.

The main way libblkid works is by reading data directly from block devices. For disks, this means reading the partition table. For filesystems, this means reading from the filesystem superblock. Theoretically, if you can't use libblkid (which is highly unusual, even for stripped-down systems) you can parse the partition table or superblock yourself.

Libblkid does use a cache file at /etc/blkid.tab but this is just a cache and it is not necessary.

Other interfaces to UUIDs, like /dev/disk/by-uuid, are created in user-space by programs which use libblkid.

Filesystem UUIDs

Normally, if you are talking about the UUIDs you see in /dev/disk/by-uuid, which are the same UUIDs you use in /etc/fstab, you are talking about the filesystem UUID. The libblkid library does the hard work of decoding the superblocks for a wide array of filesystems. You can see, for example, the UUID for Ext2 filesystems appear in libblkid src/superblocks/ext.c line 48. Libblkid supports many filesystem types and you can see them in the src/superblocks directory.

Not all filesystem types have UUIDs. Ext2 has a UUID, FAT32 does not, but it has a different ID which serves the same purpose.

Partition UUIDs

If you really need the unique partition UUID, you can also get that. Libblkid reads this out of the partition table. Only certain types of partition tables have UUIDs for partitions at all. GPT does, but DOS partition tables do not.

Using libblkid to find UUID of a partition

It's pretty much as simple as the manual makes it look: you create a probe structure, initialize it, ask it for some information, and then free it. And you can combine the first two steps into one. This is a working program:

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <blkid/blkid.h>

int main (int argc, char *argv[]) {
blkid_probe pr;
const char *uuid;

if (argc != 2) {
fprintf(stderr, "Usage: %s devname\n", argv[0]);
exit(1);
}

pr = blkid_new_probe_from_filename(argv[1]);
if (!pr) {
err(2, "Failed to open %s", argv[1]);
}

blkid_do_probe(pr);
blkid_probe_lookup_value(pr, "UUID", &uuid, NULL);

printf("UUID=%s\n", uuid);

blkid_free_probe(pr);

return 0;
}

blkid_probe_lookup_value sets uuid to point to a string that belongs to the pr structure, which is why the argument is of type const char *. If you needed to, you could copy it to a char * that you manage on your own, but for just passing to printf, that's not needed. The fourth argument to blkid_probe_lookup_value lets you get the length of the returned value in case you need that as well. There are some subtle differences between blkid_do_probe, blkid_do_safeprobe, and blkid_do_fullprobe, but in cases where the device has a known filesystem and you just want to pull the UUID out of it, taking the first result from blkid_do_probe should do.

Get HD/Partition serial number

In linux, you could use the blkid command to get the UUID of the partition:

# blkid /dev/sda1
/dev/sda1: UUID="15677362-cef3-4a53-aca3-3bace1b0d92a" TYPE="ext4"

This info is stored in the formatting of specific partition types like ext4, xfs and changes when reformatted. There is no info available for unformatted partitions.

If you need to call it from code, calling out to a shell to run this command isn't exactly the prettiest way to do it, but it could work:

#include <stdio.h>

int main(int argc,char ** argv) {

/* device you are looking for */
char device[]="/dev/sda1";

/* buffer to hold info */
char buffer[1024];

/* format into a single command to be run */
sprintf(buffer,"/sbin/blkid -o value %s",device);

/* run the command via popen */
FILE *f=popen(buffer,"r");

/* probably should check to make sure f!=null */

/* read the first line of output */
fgets(buffer,sizeof(buffer),f);

/* print the results (note, newline is included in string) */
fprintf(stdout,"uuid is %s",buffer);

}

How to get the block device name for a file?

Here is the hideous script I created to do this.
Please let there be a better way!

#!/bin/bash
#
# Given a file identify the block device on which it resides
#

PROGNAME=`basename $0`
STATUS=0
VERBOSE=0

showUsage() {
echo "usage:"
echo " $PROGNAME <filename>"
}

showHelp() {
showUsage
echo
echo "$PROGNAME is a simple utility to identify the block device on which a file resides"
}

usageError() {
if [ -z "$1" ]; then
echo "$PROGNAME: error: incorrect usage" >&2
else
echo "$PROGNAME: error: incorrect usage: $1" >&2
fi
showUsage >&2
exit 1
}

reportError() {
echo "$PROGNAME: error: $1" >&2
STATUS=1
}

reportFileError() {
echo "$1: error: $2" >&2
STATUS=1
}

checkStatus() {
RET=$?
if [ $RET -ne 0 ]; then
STATUS=$RET
reportError "$1"
exit $RET
fi
}

while [ $# -gt 0 ]; do
case "$1" in
--)
shift
break;;
--help)
showHelp
exit 0
break;;
--verbose)
VERBOSE=1
shift;;
--*)
usageError "unknown option \"$1\""
shift;;
*)
break;;
esac
done

FILE="$1"
if [ $VERBOSE -eq 1 ]; then
echo FILE=$FILE
fi
FILE=`readlink -f $FILE`

if [ -z "$1" ]; then
usageError "expected a filename"
exit 1
elif [ ! -e $FILE ]; then
reportFileError "$FILE" "file does not exist"
exit 1
fi

FILESYSTEM=`findmnt -no source -T $FILE`
checkStatus "could not identify mount point"

if [ $VERBOSE -eq 1 ]; then
echo FILESYSTEM=$FILESYSTEM
fi

FS=`basename $FILESYSTEM`
SYSLINE=`grep -rs $FS /sys/devices 2>/dev/null`
test ! -z "$SYSLINE"
checkStatus "could not find device in /sys/devices"

if [ $FS == "fuse" ]; then
reportFileError "$FILE" "is on a fuse filesystem"
exit 1
fi

echo $SYSLINE | grep -q virtual
if [ $? -eq 0 ]; then
LOGICALVOLUME=1
if [ $VERBOSE -eq 1 ]; then
echo "file is on a logical volume"
fi
else
LOGICALVOLUME=0

fi

DEVICENO=`lsblk | grep $FS | awk '{ print $2; }' | head -n 1`
checkStatus "could identify device number"
test ! -z $DEVICENO
checkStatus "could identify device number"

if [ $VERBOSE -eq 1 ]; then
echo "Device number $DEVICENO"
fi

if [ $LOGICALVOLUME -eq 1 ]; then
DEVDIR=`grep -rs ${DEVICENO}$ /sys/devices/ 2>&1`
DEVDIR=`dirname $DEVDIR`
DEVICE=`ls $DEVDIR/slaves | head -n 1`
if [ $VERBOSE -eq 1 ]; then
echo DEVICE=/dev/$DEVICE
fi
DEVICENO=`lsblk | grep $DEVICE | awk '{ print $2; }' | head -n 1`
checkStatus "could identify parent device number"
if [ $VERBOSE -eq 1 ]; then
echo "Parent Device number $DEVICENO"
fi
fi

# The first grep does not like the first colon so we double grep
# Maybe a /sys pecularity?
DEVLINE=`grep -rs ${DEVICENO}$ /sys/devices/ 2>&1 | grep :${DEVICENO}`
checkStatus "could identify device from number in sys"

DEVICE=`echo $DEVLINE | sed -e s_.*/block/__ | cut -f 1 -d/`
if [ $VERBOSE -eq 1 ]; then
echo -n "block device: "
fi
echo "/dev/$DEVICE"

exit $STATUS

This appears to work for any normal file or directory
Here is some example output.

Where /home is a logical volume.

>./getblkdev.sh --verbose /home
FILE=/home
FILESYSTEM=/dev/mapper/rhel-home
file is on a logical volume
Device number 253:2
DEVICE=/dev/sda2
Parent Device number 8:2
block device: /dev/sda

Where /boot is a regular partition:

>./getblkdev.sh --verbose /boot
FILE=/boot
FILESYSTEM=/dev/sda1
Device number 8:1
block device: /dev/sda

Please suggest improvements in comments or with your own alternative answers be they scripts, programs in other languages or even pseudo-code with a better plan.

how to determine what sectors of partition are unused?

Yes, this is possible. However, it isn't quite as easy as you might think.
You cannot simply look for a "start" and "end" sector, as the existing file system might have scattered its data all over the partition. The right thing to do is the following:

Determine how much free space is left in the file system, and how much you could possibly take away (the file system might have some expectations about that).Then, move all the data to the start of the partition, making sure the file system understands the data is in new places.

After that, you can alter the size of the partition itself, and create new partitions.

All together, that is not a trivial task, and is best done by partition managers that can do file system re-sizing. In your case, you want one that understands NTFS and probably also is standalone, i.e. runs from a live CD or something like that - I'm not sure you can resize a NTFS file system that is currently being used. Arch Linux might even provide a "boot" or "install" DVD that will do this.



Related Topics



Leave a reply



Submit