/*
 * fsbyname.c - Find a file system by name (label or UUID)
 *
 * Written 2005, 2007 by Werner Almesberger
 */

#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>

typedef uint8_t __u8;
typedef uint16_t __u16;
typedef uint32_t __u32;
typedef uint16_t __le16;
typedef uint32_t __le32;

#include "ext2_sb.h"
#include "ext3_sb.h"
#include "magic.h"

#define SB_OFFSET	1024
#define SB_SIZE		1024


static int uuidcmp(const char *s,const uint8_t *uuid,size_t uuid_len)
{
    while (uuid_len) {
	char hex[3] = "xx";
	uint8_t byte;
	char *end;

	if (!s[0] || !s[1])
	    return -1;
	hex[0] = s[0];
	hex[1] = s[1];
	byte = strtoul(hex,&end,16);
	if (*end) { /* skip separator */
	    s++;
	    continue;
	}
	if (byte != *uuid)
	    return -1;
	s += 2;
	uuid++;
	uuid_len--;
    }
    return *s;
}


static uint16_t get_le16(const __le16 *__p)
{
    const uint8_t *p = (const uint8_t *) __p;

    return 0[p] | (p[1] << 8);
}


static void usage(const char *name)
{
    fprintf(stderr,"usage: %s [-l label] [-u uuid] device ...\n",name);
    exit(1);
}


int main(int argc,char **argv)
{
    const char *label = NULL;
    const char *uuid = NULL;
    uint8_t buffer[SB_SIZE];
    const struct ext2_super_block *ext2 = (struct ext2_super_block *) buffer;
    const struct ext3_super_block *ext3 = (struct ext3_super_block *) buffer;
    int i;

    for (i = 1; i < argc; i++) {
	int fd,got;

	if (argv[i][0] == '-') {
	    switch (argv[i][1]) {
		case 'l':
		    if (argv[i][2])
			label = argv[i]+2;
		    else {
			label = argv[++i];
			if (!label)
			    usage(argv[0]);
		    }
		    continue;
		case 'u':
		    if (argv[i][2])
			uuid = argv[i]+2;
		    else {
			uuid = argv[++i];
			if (!uuid)
			    usage(argv[0]);
		    }
		    continue;
		default:
		    usage(argv[0]);
	    }
	}
	fd = open(argv[i],O_RDONLY);
	if (fd < 0) {
	    perror(argv[i]);
	    continue;
	}
	if (lseek(fd,SB_OFFSET,SEEK_SET) == (off_t) -1) {
	    perror(argv[1]);
	    goto fail;
	}
	got = read(fd,buffer,SB_SIZE);
	if (got < 0) {
	    perror(argv[1]);
	    goto fail;
	}
	if (got != SB_SIZE) {
	    fprintf(stderr,"%s: bad read (%d != %d)\n",argv[i],got,SB_SIZE);
	    goto fail;
	}
	if (get_le16(&ext2->s_magic) == EXT2_SUPER_MAGIC) {
	    if (label &&
	      !strncmp(label,ext2->s_volume_name,sizeof(ext2->s_volume_name)))
		  goto found;
	    if (uuid &&
	      !uuidcmp(uuid,ext2->s_uuid,sizeof(ext2->s_uuid)))
		  goto found;
	}
	if (get_le16(&ext3->s_magic) == EXT3_SUPER_MAGIC) {
	    if (label &&
	      !strncmp(label,ext3->s_volume_name,sizeof(ext3->s_volume_name)))
		  goto found;
	    if (uuid &&
	      !uuidcmp(uuid,ext3->s_uuid,sizeof(ext3->s_uuid)))
		  goto found;
	}
fail:
	(void) close(fd);
    }
    return 1;

found:
    printf("%s\n",argv[i]);
    return 0;
}
