Index: uspace/lib/gpt/libgpt.c
===================================================================
--- uspace/lib/gpt/libgpt.c	(revision cbd640572786d277a31b5a1f8f88967e8962afd4)
+++ uspace/lib/gpt/libgpt.c	(revision cbd640572786d277a31b5a1f8f88967e8962afd4)
@@ -0,0 +1,426 @@
+/*
+ * Copyright (c) 2011 Dominik Taborsky
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ *   notice, this list of conditions and the following disclaimer.
+ * - Redistributions in binary form must reproduce the above copyright
+ *   notice, this list of conditions and the following disclaimer in the
+ *   documentation and/or other materials provided with the distribution.
+ * - The name of the author may not be used to endorse or promote products
+ *   derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/** @addtogroup libgpt
+ * @{
+ */
+/** @file
+ */
+
+/* TODO:
+ * This implementation only supports fixed size partition entries. Specification
+ * requires otherwise, though. Use void * array and casting to achieve that.
+ */
+
+#include <checksum.h>
+#include <errno.h>
+#include <mem.h>
+
+#include "libgpt.h"
+
+/** Read GPT from specific device
+ * @param	dev_handle	device to read GPT from
+ * 
+ * @return				GPT record on success, NULL on error
+ */
+gpt_t * gpt_read_gpt_header(service_id_t dev_handle)
+{
+	int rc;
+	size_t b_size;
+	
+	rc = block_get_bsize(dev_handle, &b_size);
+	if (rc != EOK) {
+		errno = rc;
+		return NULL;
+	}
+	
+	gpt_t * gpt = malloc(sizeof(gpt_t));
+	if (gpt == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	gpt->header_lba = malloc(b_size);// We might need only sizeof(gpt_header_t),
+	if (gpt == NULL) {				// but we should follow specs and have 
+		free(gpt);					// zeroes through all the rest of the block
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	
+	rc = load_and_check_header(handle, GPT_HDR_BA, gpt->header_lba);
+	if (rc == EBADCHECKSUM || rc == EINVAL) {
+		aoff64_t n_blocks;
+		rc = block_get_nblocks(dev_handle, &n_blocks);
+		if (rc != EOK) {
+			errno = rc;
+			goto fail;
+		}
+		
+		rc = load_and_check_header(handle, n_blocks - 1, gpt->header_lba);
+		if (rc == EBADCHECKSUM || rc == EINVAL) {
+			errno = rc;
+			goto fail;
+		}
+	}
+	
+	return gpt;
+
+fail:
+	gpt_free_gpt(gpt);
+	return NULL;
+}
+
+/** Write GPT header to device
+ * @param header		GPT header to be written
+ * @param dev_handle	device handle to write the data to
+ * 
+ * @return				0 on success, libblock error code otherwise
+ */
+int gpt_write_gpt_header(gpt_t * header, service_id_t dev_handle)
+{
+	int rc;
+	size_t b_size;
+	
+	header->raw_data->header_crc32 = 0;
+	header->raw_data->header_crc32 = compute_crc32(header, header->header_size);
+	
+	rc = block_get_bsize(dev_handle, &b_size);
+	if (rc != EOK)
+		return rc;
+	
+	rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
+	if (rc != EOK)
+		return rc;
+	
+	/* Write to main GPT header location */
+	rc = block_write_direct(dev_handle, GPT_HDR_BA, GPT_HDR_BS, header->raw_data);
+	if (rc != EOK)
+		block_fini(dev_handle);
+		return rc;
+	
+	aoff64_t n_blocks;
+	rc = block_get_nblocks(dev_handle, &n_blocks);
+	if (rc != EOK)
+		return rc;
+	
+	/* Write to backup GPT header location */
+	rc = block_write_direct(dev_handle, n_blocks - 1, GPT_HDR_BS, header->raw_data);
+	block_fini(dev_handle);
+	if (rc != EOK)
+		return rc;
+		
+	return 0;
+}
+
+/** Parse partitions from GPT
+ * @param gpt	GPT to be parsed
+ * 
+ * @return 		partition linked list pointer or NULL on error
+ * 				error code is stored in errno
+ */
+gpt_parts_t * gpt_read_partitions(gpt_t * gpt)
+{
+	int i;
+	gpt_parts_t * res;
+	uint32_t num_ent = uint32_t_le2host(gpt->header.num_entries);
+	uint32_t ent_size = uint32_t_le2host(gpt->header.entry_size);
+	uint64_t ent_lba = uint64_t_le2host(gpt->header.entry_lba);
+	
+	res = alloc_part_array(num_ent);
+	if (res == NULL) {
+		//errno = ENOMEM; // already set in alloc_part_array()
+		return NULL;
+	}
+	
+	/* We can limit comm_size like this:
+	 *  - we don't need more bytes
+	 *  - the size of GPT partition entry can be different to 128 bytes */
+	rc = block_init(EXCHANGE_SERIALIZE, dev_handle, sizeof(gpt_entry_t));
+	if (rc != EOK) {
+		gpt_free_partitions(res);
+		errno = rc;
+		return NULL;
+	}
+	
+	size_t block_size;
+	rc = block_get_bsize(gpt->device, &block_size);
+	if (rc != EOK) {
+		gpt_free_partitions(res);
+		errno = rc;
+		return NULL;
+	}
+	
+	//size_t bufpos = 0;
+	//size_t buflen = 0;
+	aoff64_t pos = ent_lba * block_size;
+	
+	/* Now we read just sizeof(gpt_entry_t) bytes for each entry from the device.
+	 * Hopefully, this does not bypass cache (no mention in libblock.c),
+	 * and also allows us to have variable partition entry size (but we
+	 * will always read just sizeof(gpt_entry_t) bytes - hopefully they
+	 * don't break backward compatibility) */
+	for (i = 0; i < num_ent; ++i) {
+		//FIXME: this does bypass cache...
+		rc = block_read_bytes_direct(gpt->device, pos, sizeof(gpt_entry_t), res->part_array + i);
+		//FIXME: but seqread() is just too complex...
+		//rc = block_seqread(gpt->device, &bufpos, &buflen, &pos, res->part_array[i], sizeof(gpt_entry_t));
+		pos += ent_size;
+		
+		if (rc != EOK) {
+			gpt_free_partitions(res);
+			errno = rc;
+			return NULL;
+		}
+	}
+	
+	uint32_t crc = compute_crc32(res->part_array, res->num_ent * sizeof(gpt_entry_t));
+	
+	if(gpt->raw_data.pe_array_crc32 != crc)
+	{
+		gpt_free_partitions(res);
+		errno = EBADCHECKSUM;
+		return NULL;
+	}
+	
+	return res;
+}
+
+/** Write GPT and partitions to device
+ * @param parts			partition list to be written
+ * @param mbr			MBR to be written with 'parts' partitions
+ * @param dev_handle	device to write the data to
+ * 
+ * @return				returns EOK on succes, specific error code otherwise
+ */
+int gpt_write_partitions(gpt_parts_t * parts, gpt_t * header, service_id_t dev_handle)
+{
+	int i;
+	size_t b_size;
+	
+	header->raw_data->pe_array_crc32 = compute_crc32(parts->part_array, parts->num_ent * header->raw_data->entry_size);
+	
+	rc = block_get_bsize(dev_handle, &b_size);
+	if (rc != EOK)
+		return rc;
+	
+	rc = block_init(EXCHANGE_ATOMIC, dev_handle, b_size);
+	if (rc != EOK)
+		return rc;
+	
+	/* Write to main GPT partition array location */
+	rc = block_write_direct(dev_handle, uint64_t_le2host(header->entry_lba), 
+			nearest_larger_int((uint64_t_le2host(header->raw_data->entry_size) * parts->num_ent) / b_size), 
+			parts->part_array);
+	if (rc != EOK)
+		block_fini(dev_handle);
+		return rc;
+	
+	aoff64_t n_blocks;
+	rc = block_get_nblocks(dev_handle, &n_blocks);
+	if (rc != EOK)
+		return rc;
+	
+	/* Write to backup GPT partition array location */
+	//rc = block_write_direct(dev_handle, n_blocks - 1, GPT_HDR_BS, header->raw_data);
+	block_fini(dev_handle);
+	if (rc != EOK)
+		return rc;
+	
+	
+	gpt_write_gpt_header(header, dev_handle);
+	
+	return 0;
+	
+}
+
+/** free() GPT header including gpt->header_lba */
+void gpt_free_gpt(gpt_t * gpt)
+{
+	free(gpt->header_lba);
+	free(gpt);
+}
+
+/** Free partition list
+ * 
+ * @param parts		partition list to be freed
+ */
+void gpt_free_partitions(gpt_parts_t * parts)
+{
+	free(parts->part_array);
+	free(parts);
+}
+
+/** Set partition type
+ * @param p			partition to be set
+ * @param type		partition type to set
+ * 					- see gpt_ptypes to choose from
+ * 
+ */
+void gpt_set_part_type(g_part_t * p, int type)
+{
+	/* Beware: first 3 blocks are byteswapped! */
+	p->raw_data.part_type[3] = gpt_ptypes[type][0];
+	p->raw_data.part_type[2] = gpt_ptypes[type][1];
+	p->raw_data.part_type[1] = gpt_ptypes[type][2];
+	p->raw_data.part_type[0] = gpt_ptypes[type][3];
+	
+	p->raw_data.part_type[5] = gpt_ptypes[type][4];
+	p->raw_data.part_type[4] = gpt_ptypes[type][5];
+	
+	p->raw_data.part_type[7] = gpt_ptypes[type][6];
+	p->raw_data.part_type[6] = gpt_ptypes[type][7];
+	
+	p->raw_data.part_type[8] = gpt_ptypes[type][8];
+	p->raw_data.part_type[9] = gpt_ptypes[type][9];
+	p->raw_data.part_type[10] = gpt_ptypes[type][10];
+	p->raw_data.part_type[11] = gpt_ptypes[type][11];
+	p->raw_data.part_type[12] = gpt_ptypes[type][12];
+	p->raw_data.part_type[13] = gpt_ptypes[type][13];
+	p->raw_data.part_type[14] = gpt_ptypes[type][14];
+	p->raw_data.part_type[15] = gpt_ptypes[type][15];
+}
+
+/** Copy partition name */
+void gpt_set_part_name(gpt_entry_t * p, char_t name[], size_t length)
+{
+	memcpy(p->part_name, name, length);
+}
+
+// Internal functions follow //
+
+static int load_and_check_header(service_id_t handle, aoff64_t addr, gpt_t * header)
+{
+	rc = block_init(EXCHANGE_ATOMIC, handle, b_size);
+	if (rc != EOK)
+		return rc;
+	
+	rc = block_read_direct(handle, addr, GPT_HDR_BS, gpt->header_lba);
+	block_fini(handle);
+	if (rc != EOK)
+		return rc;
+	
+	
+	int i;
+	/* Check the EFI signature */
+	for (i = 0; i < 8; ++i) {
+		if (gpt->header_lba.efi_signature[i] != efi_signature[i])
+			return EINVAL;
+	}
+	
+	/* Check the CRC32 of the header */
+	uint32_t crc = gpt->header_lba->header_crc32;
+	gpt->header_lba->header_crc32 = 0;
+	if (crc != compute_crc32(gpt->header_lba, gpt->header_lba->header_size))
+		return EBADCHECKSUM;
+	else
+		gpt->header_lba->header_crc32 = crc;
+	
+	/* Check for zeroes in the rest of the block */
+	for (i = sizeof(gpt_header_t); i < b_size; ++i) {
+		if (((uint8_t *) gpt->header_lba)[i] != 0)
+			goto EINVAL;
+	}
+	
+	gpt->device = dev_handle;
+	//gpt->partitions = NULL;
+	
+	return EOK;
+}
+
+static gpt_parts_t * alloc_part_array(uint32_t num)
+{
+	gpt_parts_t * res = malloc(sizeof(gpt_parts_t));
+	if (res == NULL) {
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	uint32_t size = num > GPT_BASE_PART_NUM ? num : GPT_BASE_PART_NUM;
+	res->part_array = malloc(size * sizeof(gpt_entry_t));
+	if (res.part_array == NULL) {
+		free(res);
+		errno = ENOMEM;
+		return NULL;
+	}
+	
+	res->num_ent = num;
+	res->arr_size = size;
+	
+	return res;
+}
+
+static int extend_part_array(gpt_parts_t * p)
+{
+	unsigned int nsize = p->arr_size * 2;
+	gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
+	if(tmp == NULL) {
+		errno = ENOMEM;
+		return -1;
+	}
+	
+	memcpy(tmp, p->part_array, p->num_ent);
+	free(p->part_array);
+	p->part_array = tmp;
+	p->arr_size = nsize;
+	
+	return 0;
+}
+
+static int reduce_part_array(gpt_parts_t * p)
+{
+	if(p->arr_size > GPT_MIN_PART_NUM) {
+		unsigned int nsize = p->arr_size / 2;
+		gpt_entry_t * tmp = malloc(nsize * sizeof(gpt_entry_t));
+		if(tmp == NULL) {
+			errno = ENOMEM;
+			return -1;
+		}
+		
+		memcpy(tmp, p->part_array, p->num_ent < nsize ? p->num_ent : nsize);
+		free(p->part_array);
+		p->part_array = tmp;
+		p->arr_size = nsize;
+	}
+	
+	return 0;
+}
+
+//FIXME: replace this with a library call, if it exists
+static long long nearest_larger_int(double a)
+{
+	if ((long long) a == a) {
+		return (long long) a;
+	}
+	
+	return ((long long) a) + 1;
+}
+
+
+
+
