[patch] support partiton labels/uuids of common filesystems

Kay Sievers kay.sievers at vrfy.org
Fri Apr 30 03:59:25 PDT 2004


Hi,
attached is a patch to support labels of msdos, vfat, ext2/3,
reiser, jfs, xfs partitions. ntfs is only recognized.

The following properties are set if a valid value is found:
	block.fstype
	block.volume_label
	block.volume.uuid

Plese have a look, if you like it.

thanks,
Kay
-------------- next part --------------
diff -urN a/hal/hald/Makefile.am b/hal/hald/Makefile.am
--- a/hal/hald/Makefile.am	26 Apr 2004 16:52:54 -0000
+++ b/hal/hald/Makefile.am	30 Apr 2004 10:02:20 -0000
@@ -61,6 +61,9 @@
                                         linux/libsysfs/sysfs_driver.c   \
                                         linux/libsysfs/sysfs_utils.c
 
+hald_SOURCES +=                                                         \
+	linux/volume_id/volume_id.h	linux/volume_id/volume_id.c
+
 hald_LDADD = @PACKAGE_LIBS@
 
 #### Init scripts fun
diff -urN a/hal/hald/linux/block_class_device.c b/hal/hald/linux/block_class_device.c
--- a/hal/hald/linux/block_class_device.c	26 Apr 2004 20:08:58 -0000
+++ b/hal/hald/linux/block_class_device.c	30 Apr 2004 10:02:22 -0000
@@ -57,6 +57,7 @@
 #include "class_device.h"
 #include "common.h"
  
+#include "volume_id/volume_id.h"
 #include "linux_dvd_rw_utils.h"
 
 /**
@@ -587,70 +588,31 @@
 	}
 }
 
-static dbus_bool_t
-detect_fs_fat (HalDevice *d)
+static void
+detect_fs (HalDevice *d)
 {
-	int i, len;
-	int fd;
+	struct volume_id *vid;
 	const char *device_file;
-	unsigned char data[512];
-	char label[12];
-	dbus_bool_t matched = FALSE;
-
-	/* See http://www.win.tue.nl/~aeb/linux/fs/fat/fat-1.html for
-	 * more information
-	 */
+	int rc;
 
 	device_file = hal_device_property_get_string (d, "block.device");
+	vid = volume_id_open_node(device_file);
+	if (vid == NULL)
+		return;
 
-	fd = open (device_file, O_RDONLY);
-	if (fd < 0)
-		return FALSE;
+	rc = volume_id_probe(vid, ALL);
+	if (rc != 0)
+		return;
 
-	if (512 != read (fd, data, 512))
-		goto out;
+	hal_device_property_set_string (d, "block.fstype", vid->fs_name);
+	if (vid->label_string[0] != '\0')
+		hal_device_property_set_string (d, "block.volume_label", vid->label_string);
+	if (vid->uuid_string[0] != '\0')
+		hal_device_property_set_string (d, "block.volume_uuid", vid->uuid_string);
 
-	/* signature must be 0x55aa on the last two bytes of the first 512
-	 * byte sector */
-	if (data[510] != 0x55 && 
-	    data[511] != 0xaa)
-		goto out;
-
-	memset (label, 0, 12);
-
-	if (data[82] == 'F' &&
-	    data[83] == 'A' &&
-	    data[84] == 'T' &&
-	    data[85] == '3' &&
-	    data[86] == '2' ) {
-		/* FAT32 */
-		memcpy (label, data+71, 11);
-		hal_device_property_set_string (d, "block.fstype", "vfat");
-		matched = TRUE;
-	} else if (data[54] == 'F' &&
-		   data[55] == 'A' &&
-		   data[56] == 'T' ) {
-		/* FAT12/FAT16/FAT */
-		memcpy (label, data+43, 11);
-		hal_device_property_set_string (d, "block.fstype", "vfat");
-		matched = TRUE;
-	}
-
-	len = strlen (label);
-	for (i=len-1; i>=0 && isspace (label[i]); --i)
-		label[i] = '\0';
-	hal_device_property_set_string (d, "block.volume_label", label);
-	
-out:
-	close (fd);
-	return matched;
-}
+	volume_id_close(vid);
 
-static void
-detect_fs (HalDevice *d)
-{
-	if (detect_fs_fat(d))
-		return;
+	return;
 }
 
 static void 
diff -urN a/hal/hald/linux/volume_id/volume_id.c b/hal/hald/linux/volume_id/volume_id.c
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ b/hal/hald/linux/volume_id/volume_id.c	2004-04-29 22:16:03.000000000 +0200
@@ -0,0 +1,550 @@
+/*
+ * volume_id - reads filesystem label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers at vrfy.org>
+ *
+ *	The superblock structs are taken from the libblkid living inside
+ *	the e2fsprogs. This is a simple straightforward implementation for
+ *	reading the label strings of only the most common filesystems.
+ *	If you need a full featured library with attribute caching, support for
+ *	much more partition/media types or non-root data access, you may have
+ *	a look at:
+ *		http://e2fsprogs.sourceforge.net.
+ *
+ *	This program is free software; you can redistribute it and/or modify it
+ *	under the terms of the GNU General Public License as published by the
+ *	Free Software Foundation version 2 of the License.
+ *
+ *	This program is distributed in the hope that it will be useful, but
+ *	WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *	General Public License for more details.
+ *
+ *	You should have received a copy of the GNU General Public License along
+ *	with this program; if not, write to the Free Software Foundation, Inc.,
+ *	675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <asm/types.h>
+
+#include "volume_id.h"
+
+
+#define bswap32(x) (__u32)((((__u32)(x) & 0xff000000u) >> 24) | \
+			   (((__u32)(x) & 0x00ff0000u) >>  8) | \
+			   (((__u32)(x) & 0x0000ff00u) <<  8) | \
+			   (((__u32)(x) & 0x000000ffu) << 24))
+
+#if (__BYTE_ORDER == __LITTLE_ENDIAN) 
+#define cpu_to_le32(x) (x)
+#elif (__BYTE_ORDER == __BIG_ENDIAN)
+#define cpu_to_le32(x) bswap32(x)
+#endif
+
+#define VOLUME_ID_BUFFER_SIZE		0x11000 /* reiser offset is 64k */
+
+
+static void set_label(struct volume_id *id, char *buf, int count)
+{
+	int i;
+
+	memcpy(id->label, buf, count);
+
+	memcpy(id->label_string, buf, count);
+
+	/* remove trailing whitespace */
+	i = strlen(id->label_string);
+	while (i--) {
+		if (! isspace(id->label_string[i]))
+			break;
+	}
+	id->label_string[i+1] = '\0';
+}
+
+static void set_uuid(struct volume_id *id, unsigned char *buf, int count)
+{
+	int i;
+
+	memcpy(id->uuid, buf, count);
+
+	/* create string if uuid is set */
+	for (i = 0; i < count; i++) 
+		if (buf[i] != 0)
+			goto set;
+	return;
+
+set:
+	switch(count) {
+	case 4:
+		sprintf(id->uuid_string, "%02X%02X-%02X%02X",
+			buf[3], buf[2], buf[1], buf[0]);
+		break;
+	case 16:
+		sprintf(id->uuid_string,
+			"%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+			buf[0], buf[1], buf[2], buf[3],
+			buf[4], buf[5],
+			buf[6], buf[7],
+			buf[8], buf[9],
+			buf[10], buf[11], buf[12], buf[13], buf[14],buf[15]);
+		break;
+	}
+}
+
+static int open_superblock(struct volume_id *id)
+{
+	/* get buffer to read the first block */
+	if (id->buf == NULL) {
+		id->buf = malloc(VOLUME_ID_BUFFER_SIZE);
+		if (id->buf == NULL)
+			return -1;
+	}
+
+	/* try to read the first 64k, but at least the first block */
+	memset(id->buf, 0x00, VOLUME_ID_BUFFER_SIZE);
+	lseek(id->fd, 0, SEEK_SET);
+	if (read(id->fd, id->buf, VOLUME_ID_BUFFER_SIZE) < 0x200)
+		return -1;
+
+	return 0;
+}
+
+static void close_superblock(struct volume_id *id)
+{
+	if (id->buf != NULL) {
+		free(id->buf);
+		id->buf = NULL;
+	}
+}
+
+#define EXT3_FEATURE_COMPAT_HAS_JOURNAL		0x00000004
+#define EXT3_FEATURE_INCOMPAT_JOURNAL_DEV	0x00000008
+#define EXT_SUPERBLOCK_OFFSET			0x400
+static int probe_ext(struct volume_id *id)
+{
+	struct ext2_super_block {
+		__u32		s_inodes_count;
+		__u32		s_blocks_count;
+		__u32		s_r_blocks_count;
+		__u32		s_free_blocks_count;
+		__u32		s_free_inodes_count;
+		__u32		s_first_data_block;
+		__u32		s_log_block_size;
+		__u32		s_dummy3[7];
+		unsigned char	s_magic[2];
+		__u16		s_state;
+		__u32		s_dummy5[8];
+		__u32		s_feature_compat;
+		__u32		s_feature_incompat;
+		__u32		s_feature_ro_compat;
+		unsigned char	s_uuid[16];
+		char		s_volume_name[16];
+	} *es;
+
+	es = (struct ext2_super_block *) (id->buf + EXT_SUPERBLOCK_OFFSET);
+
+	if (es->s_magic[0] != 0123 ||
+	    es->s_magic[1] != 0357)
+		return -1;
+
+	set_label(id, es->s_volume_name, 16);
+	set_uuid(id, es->s_uuid, 16);
+
+	if ((cpu_to_le32(es->s_feature_compat) & EXT3_FEATURE_COMPAT_HAS_JOURNAL) != 0) {
+		id->fs_type = EXT3;
+		id->fs_name = "ext3";
+	} else {
+		id->fs_type = EXT2;
+		id->fs_name = "ext2";
+	}
+
+	return 0;
+}
+
+#define REISER1_SUPERBLOCK_OFFSET		0x2000
+#define REISER_SUPERBLOCK_OFFSET		0x10000
+static int probe_reiser(struct volume_id *id)
+{
+	struct reiser_super_block {
+		__u32		rs_blocks_count;
+		__u32		rs_free_blocks;
+		__u32		rs_root_block;
+		__u32		rs_journal_block;
+		__u32		rs_journal_dev;
+		__u32		rs_orig_journal_size;
+		__u32		rs_dummy2[5];
+		__u16		rs_blocksize;
+		__u16		rs_dummy3[3];
+		unsigned char	rs_magic[12];
+		__u32		rs_dummy4[5];
+		unsigned char	rs_uuid[16];
+		char		rs_label[16];
+	} *rs;
+
+	rs = (struct reiser_super_block *) &(id->buf[REISER1_SUPERBLOCK_OFFSET]);
+
+	if (strncmp(rs->rs_magic, "ReIsErFs", 8) == 0)
+		goto found;
+
+	rs = (struct reiser_super_block *) &(id->buf[REISER_SUPERBLOCK_OFFSET]);
+
+	if (strncmp(rs->rs_magic, "ReIsEr2Fs", 9) == 0)
+		goto found;
+	if (strncmp(rs->rs_magic, "ReIsEr3Fs", 9) == 0)
+		goto found;
+
+	return -1;
+
+found:
+	set_label(id, rs->rs_label, 16);
+	set_uuid(id, rs->rs_uuid, 16);
+
+	id->fs_type = REISER;
+	id->fs_name = "reiser";
+
+	return 0;
+}
+
+static int probe_xfs(struct volume_id *id)
+{
+	struct xfs_super_block {
+		unsigned char	xs_magic[4];
+		__u32		xs_blocksize;
+		__u64		xs_dblocks;
+		__u64		xs_rblocks;
+		__u32		xs_dummy1[2];
+		unsigned char	xs_uuid[16];
+		__u32		xs_dummy2[15];
+		char		xs_fname[12];
+		__u32		xs_dummy3[2];
+		__u64		xs_icount;
+		__u64		xs_ifree;
+		__u64		xs_fdblocks;
+	} *xs;
+
+	xs = (struct xfs_super_block *) id->buf;
+
+	if (strncmp(xs->xs_magic, "XFSB", 4) != 0)
+		return -1;
+
+	set_label(id, xs->xs_fname, 12);
+	set_uuid(id, xs->xs_uuid, 16);
+
+	id->fs_type = XFS;
+	id->fs_name = "xfs";
+
+	return 0;
+}
+
+#define JFS_SUPERBLOCK_OFFSET			0x8000
+static int probe_jfs(struct volume_id *id)
+{
+	struct jfs_super_block {
+		unsigned char	js_magic[4];
+		__u32		js_version;
+		__u64		js_size;
+		__u32		js_bsize;
+		__u32		js_dummy1;
+		__u32		js_pbsize;
+		__u32		js_dummy2[27];
+		unsigned char	js_uuid[16];
+		unsigned char	js_label[16];
+		unsigned char	js_loguuid[16];
+	} *js;
+
+	js = (struct jfs_super_block *) &(id->buf[JFS_SUPERBLOCK_OFFSET]);
+
+	if (strncmp(js->js_magic, "JFS1", 4) != 0)
+		return -1;
+
+	set_label(id, js->js_label, 16);
+	set_uuid(id, js->js_uuid, 16);
+
+	id->fs_type = JFS;
+	id->fs_name = "jfs";
+
+	return 0;
+}
+
+static int probe_vfat(struct volume_id *id)
+{
+	struct vfat_super_block {
+		unsigned char	vs_ignored[3];
+		unsigned char	vs_sysid[8];
+		unsigned char	vs_sector_size[2];
+		__u8		vs_cluster_size;
+		__u16		vs_reserved;
+		__u8		vs_fats;
+		unsigned char	vs_dir_entries[2];
+		unsigned char	vs_sectors[2];
+		unsigned char	vs_media;
+		__u16		vs_fat_length;
+		__u16		vs_secs_track;
+		__u16		vs_heads;
+		__u32		vs_hidden;
+		__u32		vs_total_sect;
+		__u32		vs_fat32_length;
+		__u16		vs_flags;
+		__u8		vs_version[2];
+		__u32		vs_root_cluster;
+		__u16		vs_insfo_sector;
+		__u16		vs_backup_boot;
+		__u16		vs_reserved2[6];
+		unsigned char	vs_unknown[3];
+		unsigned char	vs_serno[4];
+		char		vs_label[11];
+		unsigned char	vs_magic[8];
+		unsigned char	vs_dummy2[164];
+		unsigned char	vs_pmagic[2];
+	} *vs;
+
+	vs = (struct vfat_super_block *) id->buf;
+
+	if (strncmp(vs->vs_magic, "MSWIN", 5) == 0)
+		goto found;
+	if (strncmp(vs->vs_magic, "FAT32   ", 8) == 0)
+		goto found;
+	return -1;
+
+found:
+	memcpy(id->label, vs->vs_label, 11);
+	memcpy(id->uuid, vs->vs_serno, 4);
+
+	id->fs_type = VFAT;
+	id->fs_name = "vfat";
+
+	return 0;
+}
+
+static int probe_msdos(struct volume_id *id)
+{
+	struct msdos_super_block {
+		unsigned char	ms_ignored[3];
+		unsigned char	ms_sysid[8];
+		unsigned char	ms_sector_size[2];
+		__u8		ms_cluster_size;
+		__u16		ms_reserved;
+		__u8		ms_fats;
+		unsigned char	ms_dir_entries[2];
+		unsigned char	ms_sectors[2];
+		unsigned char	ms_media;
+		__u16		ms_fat_length;
+		__u16		ms_secs_track;
+		__u16		ms_heads;
+		__u32		ms_hidden;
+		__u32		ms_total_sect;
+		unsigned char	ms_unknown[3];
+		unsigned char	ms_serno[4];
+		char		ms_label[11];
+		unsigned char	ms_magic[8];
+		unsigned char	ms_dummy2[192];
+		unsigned char	ms_pmagic[2];
+	} *ms;
+
+	ms = (struct msdos_super_block *) id->buf;
+
+	if (strncmp(ms->ms_magic, "MSDOS", 5) == 0)
+		goto found;
+	if (strncmp(ms->ms_magic, "FAT16   ", 8) == 0)
+		goto found;
+	if (strncmp(ms->ms_magic, "FAT12   ", 8) == 0)
+		goto found;
+	return -1;
+
+found:
+	set_label(id, ms->ms_label, 11);
+	set_uuid(id, ms->ms_serno, 4);
+
+	id->fs_type = MSDOS;
+	id->fs_name = "msdos";
+
+	return 0;
+}
+
+static int probe_ntfs(struct volume_id *id)
+{
+	struct ntfs_super_block {
+		char jump[3];
+		char oem_id[4];
+	} *ns;
+
+	ns = (struct ntfs_super_block *) id->buf;
+
+	if (strncmp(ns->oem_id, "NTFS", 4) != 0)
+		return -1;
+
+	id->fs_type = NTFS;
+	id->fs_name = "ntfs";
+
+	return 0;
+}
+
+static int probe_swap(struct volume_id *id)
+{
+	int magic;
+
+	/* huhh, the swap signature is on the end of the PAGE_SIZE */
+	for (magic = 0x1000; magic <= 0x4000; magic <<= 1) {
+			if (strncmp(&(id->buf[magic -10]), "SWAP-SPACE", 10) == 0)
+				goto found;
+			if (strncmp(&(id->buf[magic -10]), "SWAPSPACE2", 10) == 0)
+				goto found;
+	}
+	return -1;
+
+found:
+	id->fs_type = SWAP;
+	id->fs_name = "swap";
+
+	return 0;
+}
+
+/* probe volume for filesystem type and try to read label+uuid */
+int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type)
+{
+	int rc;
+
+	if (id == NULL)
+		return -EINVAL;
+
+	if (open_superblock(id) != 0)
+		return -EACCES;
+
+	switch (fs_type) {
+	case EXT3:
+	case EXT2:
+		rc = probe_ext(id);
+		break;
+	case REISER:
+		rc = probe_reiser(id);
+		break;
+	case XFS:
+		rc = probe_xfs(id);
+		break;
+	case JFS:
+		rc = probe_jfs(id);
+		break;
+	case MSDOS:
+		rc = probe_msdos(id);
+		break;
+	case VFAT:
+		rc = probe_vfat(id);
+		break;
+	case NTFS:
+		rc = probe_ntfs(id);
+		break;
+	case SWAP:
+		rc = probe_swap(id);
+		break;
+	default:
+		rc = probe_ext(id);
+		if (rc == 0)
+			break;
+		rc = probe_reiser(id);
+		if (rc == 0)
+			break;
+		rc = probe_xfs(id);
+		if (rc == 0)
+			break;
+		rc = probe_jfs(id);
+		if (rc == 0)
+			break;
+		rc = probe_msdos(id);
+		if (rc == 0)
+			break;
+		rc = probe_vfat(id);
+		if (rc == 0)
+			break;
+		rc = probe_ntfs(id);
+		if (rc == 0)
+			break;
+		rc = probe_swap(id);
+		if (rc == 0)
+			break;
+		rc = -1;
+	}
+
+	if (rc == 0)
+		close_superblock(id);
+
+	return rc;
+}
+
+/* open volume by already open file descriptor */
+struct volume_id *volume_id_open_fd(int fd)
+{
+	struct volume_id *id;
+
+	id = malloc(sizeof(struct volume_id));
+	if (id == NULL)
+		return NULL;
+	memset(id, 0x00, sizeof(struct volume_id));
+
+	id->fd = fd;
+
+	return id;
+}
+
+/* open volume by device node */
+struct volume_id *volume_id_open_node(const char *path)
+{
+	struct volume_id *id;
+	int fd;
+
+	fd = open(path, O_RDONLY);
+	if (fd < 0)
+		return NULL;
+
+	id = volume_id_open_fd(fd);
+	if (id == NULL)
+		return NULL;
+
+	/* close fd on device close */
+	id->fd_close = 1;
+
+	return id;
+}
+
+/* open volume by major/minor */
+struct volume_id *volume_id_open_dev_t(dev_t devt)
+{
+	struct volume_id *id;
+	char tmp_node[VOLUME_ID_PATH_MAX];
+
+	snprintf(tmp_node, VOLUME_ID_PATH_MAX,
+		 "/tmp/volume-%u-%u", major(devt), minor(devt));
+	tmp_node[VOLUME_ID_PATH_MAX] = '\0';
+
+	/* create tempory node to open the block device */
+	if (mknod(tmp_node, (S_IFBLK | 0600), devt) != 0)
+		return NULL;
+
+	id = volume_id_open_node(tmp_node);
+
+	unlink(tmp_node);
+
+	return id;
+}
+
+/* free allocated volume info */
+void volume_id_close(struct volume_id *id)
+{
+	if (id == NULL)
+		return;
+
+	if (id->fd_close != 0)
+		close(id->fd);
+
+	close_superblock(id);
+
+	free(id);
+}
diff -urN a/hal/hald/linux/volume_id/volume_id.h b/hal/hald/linux/volume_id/volume_id.h
--- /dev/null	1970-01-01 01:00:00.000000000 +0100
+++ b/hal/hald/linux/volume_id/volume_id.h	2004-04-29 22:21:34.000000000 +0200
@@ -0,0 +1,72 @@
+/*
+ * volume_id - reads partition label and uuid
+ *
+ * Copyright (C) 2004 Kay Sievers <kay.sievers at vrfy.org>
+ *
+ *	This program is free software; you can redistribute it and/or modify it
+ *	under the terms of the GNU General Public License as published by the
+ *	Free Software Foundation version 2 of the License.
+ * 
+ *	This program is distributed in the hope that it will be useful, but
+ *	WITHOUT ANY WARRANTY; without even the implied warranty of
+ *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *	General Public License for more details.
+ * 
+ *	You should have received a copy of the GNU General Public License along
+ *	with this program; if not, write to the Free Software Foundation, Inc.,
+ *	675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#ifndef _VOLUME_ID_H_
+#define _VOLUME_ID_H_
+
+#define VOLUME_ID_VERSION		001
+
+#define VOLUME_ID_LABEL_SIZE		16
+#define VOLUME_ID_UUID_SIZE		16
+#define VOLUME_ID_UUID_STRING_SIZE	37
+#define VOLUME_ID_PATH_MAX		255
+
+
+enum filesystem_type {
+	ALL,
+	EXT2,
+	EXT3,
+	REISER,
+	XFS,
+	JFS,
+	MSDOS,
+	VFAT,
+	NTFS,
+	SWAP
+};
+
+struct volume_id {
+	char label[VOLUME_ID_LABEL_SIZE];
+	char label_string[VOLUME_ID_LABEL_SIZE+1];
+	unsigned char uuid[VOLUME_ID_UUID_SIZE];
+	char uuid_string[VOLUME_ID_UUID_STRING_SIZE];
+	enum filesystem_type fs_type;
+	char *fs_name;
+	int fd;
+	char *buf;
+	int fd_close;
+};
+
+/* open volume by already open file descriptor */
+extern struct volume_id *volume_id_open_fd(int fd);
+
+/* open volume by device node */
+extern struct volume_id *volume_id_open_node(const char *path);
+
+/* open volume by major/minor */
+extern struct volume_id *volume_id_open_dev_t(dev_t devt);
+
+/* probe volume for filesystem type and try to read label/uuid */
+extern int volume_id_probe(struct volume_id *id, enum filesystem_type fs_type);
+
+/* free allocated device info */
+extern void volume_id_close(struct volume_id *id);
+
+#endif
-------------- next part --------------
_______________________________________________
hal mailing list
hal at freedesktop.org
http://freedesktop.org/mailman/listinfo/hal


More information about the Hal mailing list