hal/hald/linux/volume_id volume_id.c,NONE,1.1 volume_id.h,NONE,1.1

David Zeuthen david at pdx.freedesktop.org
Fri Apr 30 10:01:25 PDT 2004


Update of /cvs/hal/hal/hald/linux/volume_id
In directory pdx:/tmp/cvs-serv30750/hald/linux/volume_id

Added Files:
	volume_id.c volume_id.h 
Log Message:
2004-04-30  David Zeuthen  <david at fubar.dk>

	* doc/TODO: update TODO note about fs detection

2004-04-30  Kay Sievers  <kay.sievers at vrfy.org>

	* hald/Makefile.am (hald_SOURCES): add volume_id/volume_id.[ch]

	* hald/linux/block_class_device.c (detect_fs): use utility in new 
	subdirectory volume_id

	* hald/linux/volume_id/volume_id.[ch]: new files



--- NEW FILE: volume_id.c ---
/*
 * 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);
}

--- NEW FILE: volume_id.h ---
/*
 * 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





More information about the hal-commit mailing list