Home | History | Annotate | Download | only in lib
      1 /* Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
      2  * Use of this source code is governed by a BSD-style license that can be
      3  * found in the LICENSE file.
      4  */
      5 
      6 #include "sysincludes.h"
      7 
      8 #include "cgptlib.h"
      9 #include "cgptlib_internal.h"
     10 #include "crc32.h"
     11 #include "gpt.h"
     12 #include "utility.h"
     13 #include "vboot_api.h"
     14 
     15 
     16 /**
     17  * Allocate and read GPT data from the drive.
     18  *
     19  * The sector_bytes and gpt_drive_sectors fields should be filled on input.  The
     20  * primary and secondary header and entries are filled on output.
     21  *
     22  * Returns 0 if successful, 1 if error.
     23  */
     24 int AllocAndReadGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
     25 {
     26 	uint64_t max_entries_bytes = MAX_NUMBER_OF_ENTRIES * sizeof(GptEntry);
     27 	int primary_valid = 0, secondary_valid = 0;
     28 
     29 	/* No data to be written yet */
     30 	gptdata->modified = 0;
     31 
     32 	/* Allocate all buffers */
     33 	gptdata->primary_header = (uint8_t *)VbExMalloc(gptdata->sector_bytes);
     34 	gptdata->secondary_header =
     35 		(uint8_t *)VbExMalloc(gptdata->sector_bytes);
     36 	gptdata->primary_entries = (uint8_t *)VbExMalloc(max_entries_bytes);
     37 	gptdata->secondary_entries = (uint8_t *)VbExMalloc(max_entries_bytes);
     38 
     39 	if (gptdata->primary_header == NULL ||
     40 	    gptdata->secondary_header == NULL ||
     41 	    gptdata->primary_entries == NULL ||
     42 	    gptdata->secondary_entries == NULL)
     43 		return 1;
     44 
     45 	/* Read primary header from the drive, skipping the protective MBR */
     46 	if (0 != VbExDiskRead(disk_handle, 1, 1, gptdata->primary_header))
     47 		return 1;
     48 
     49 	/* Only read primary GPT if the primary header is valid */
     50 	GptHeader* primary_header = (GptHeader*)gptdata->primary_header;
     51 	if (0 == CheckHeader(primary_header, 0,
     52 			gptdata->streaming_drive_sectors,
     53 			gptdata->gpt_drive_sectors,
     54 			gptdata->flags)) {
     55 		primary_valid = 1;
     56 		uint64_t entries_bytes = primary_header->number_of_entries
     57 				* primary_header->size_of_entry;
     58 		uint64_t entries_sectors = entries_bytes
     59 					/ gptdata->sector_bytes;
     60 		if (0 != VbExDiskRead(disk_handle,
     61 				      primary_header->entries_lba,
     62 				      entries_sectors,
     63 				      gptdata->primary_entries))
     64 			return 1;
     65 	} else {
     66 		VBDEBUG(("Primary GPT header invalid!\n"));
     67 	}
     68 
     69 	/* Read secondary header from the end of the drive */
     70 	if (0 != VbExDiskRead(disk_handle, gptdata->gpt_drive_sectors - 1, 1,
     71 			      gptdata->secondary_header))
     72 		return 1;
     73 
     74 	/* Only read secondary GPT if the secondary header is valid */
     75 	GptHeader* secondary_header = (GptHeader*)gptdata->secondary_header;
     76 	if (0 == CheckHeader(secondary_header, 1,
     77 			gptdata->streaming_drive_sectors,
     78 			gptdata->gpt_drive_sectors,
     79 			gptdata->flags)) {
     80 		secondary_valid = 1;
     81 		uint64_t entries_bytes = secondary_header->number_of_entries
     82 				* secondary_header->size_of_entry;
     83 		uint64_t entries_sectors = entries_bytes
     84 				/ gptdata->sector_bytes;
     85 		if (0 != VbExDiskRead(disk_handle,
     86 				      secondary_header->entries_lba,
     87 				      entries_sectors,
     88 				      gptdata->secondary_entries))
     89 			return 1;
     90 	} else {
     91 		VBDEBUG(("Secondary GPT header invalid!\n"));
     92 	}
     93 
     94 	/* Return 0 if least one GPT header was valid */
     95 	return (primary_valid || secondary_valid) ? 0 : 1;
     96 }
     97 
     98 /**
     99  * Write any changes for the GPT data back to the drive, then free the buffers.
    100  *
    101  * Returns 0 if successful, 1 if error.
    102  */
    103 int WriteAndFreeGptData(VbExDiskHandle_t disk_handle, GptData *gptdata)
    104 {
    105 	int legacy = 0;
    106 	GptHeader *header = (GptHeader *)gptdata->primary_header;
    107 	uint64_t entries_bytes = header->number_of_entries
    108 				* header->size_of_entry;
    109 	uint64_t entries_sectors = entries_bytes / gptdata->sector_bytes;
    110 	int ret = 1;
    111 
    112 	/*
    113 	 * TODO(namnguyen): Preserve padding between primary GPT header and
    114 	 * its entries.
    115 	 */
    116 	uint64_t entries_lba = GPT_PMBR_SECTORS + GPT_HEADER_SECTORS;
    117 	if (gptdata->primary_header) {
    118 		GptHeader *h = (GptHeader *)(gptdata->primary_header);
    119 		entries_lba = h->entries_lba;
    120 
    121 		/*
    122 		 * Avoid even looking at this data if we don't need to. We
    123 		 * may in fact not have read it from disk if the read failed,
    124 		 * and this avoids a valgrind complaint.
    125 		 */
    126 		if (gptdata->modified) {
    127 			legacy = !Memcmp(h->signature, GPT_HEADER_SIGNATURE2,
    128 					GPT_HEADER_SIGNATURE_SIZE);
    129 		}
    130 		if (gptdata->modified & GPT_MODIFIED_HEADER1) {
    131 			if (legacy) {
    132 				VBDEBUG(("Not updating GPT header 1: "
    133 					 "legacy mode is enabled.\n"));
    134 			} else {
    135 				VBDEBUG(("Updating GPT header 1\n"));
    136 				if (0 != VbExDiskWrite(disk_handle, 1, 1,
    137 						       gptdata->primary_header))
    138 					goto fail;
    139 			}
    140 		}
    141 	}
    142 
    143 	if (gptdata->primary_entries) {
    144 		if (gptdata->modified & GPT_MODIFIED_ENTRIES1) {
    145 			if (legacy) {
    146 				VBDEBUG(("Not updating GPT entries 1: "
    147 					 "legacy mode is enabled.\n"));
    148 			} else {
    149 				VBDEBUG(("Updating GPT entries 1\n"));
    150 				if (0 != VbExDiskWrite(disk_handle, entries_lba,
    151 						entries_sectors,
    152 						gptdata->primary_entries))
    153 					goto fail;
    154 			}
    155 		}
    156 	}
    157 
    158 	entries_lba = (gptdata->gpt_drive_sectors - entries_sectors -
    159 		GPT_HEADER_SECTORS);
    160 	if (gptdata->secondary_header) {
    161 		GptHeader *h = (GptHeader *)(gptdata->secondary_header);
    162 		entries_lba = h->entries_lba;
    163 		if (gptdata->modified & GPT_MODIFIED_HEADER2) {
    164 			VBDEBUG(("Updating GPT entries 2\n"));
    165 			if (0 != VbExDiskWrite(disk_handle,
    166 					       gptdata->gpt_drive_sectors - 1, 1,
    167 					       gptdata->secondary_header))
    168 				goto fail;
    169 		}
    170 	}
    171 
    172 	if (gptdata->secondary_entries) {
    173 		if (gptdata->modified & GPT_MODIFIED_ENTRIES2) {
    174 			VBDEBUG(("Updating GPT header 2\n"));
    175 			if (0 != VbExDiskWrite(disk_handle,
    176 				entries_lba, entries_sectors,
    177 				gptdata->secondary_entries))
    178 				goto fail;
    179 		}
    180 	}
    181 
    182 	ret = 0;
    183 
    184 fail:
    185 	/* Avoid leaking memory on disk write failure */
    186 	if (gptdata->primary_header)
    187 		VbExFree(gptdata->primary_header);
    188 	if (gptdata->primary_entries)
    189 		VbExFree(gptdata->primary_entries);
    190 	if (gptdata->secondary_entries)
    191 		VbExFree(gptdata->secondary_entries);
    192 	if (gptdata->secondary_header)
    193 		VbExFree(gptdata->secondary_header);
    194 
    195 	/* Success */
    196 	return ret;
    197 }
    198 
    199 int IsUnusedEntry(const GptEntry *e)
    200 {
    201 	static Guid zero = {{{0, 0, 0, 0, 0, {0, 0, 0, 0, 0, 0}}}};
    202 	return !Memcmp(&zero, (const uint8_t*)(&e->type), sizeof(zero));
    203 }
    204 
    205 /*
    206  * Func: GptGetEntrySize
    207  * Desc: This function returns size(in lba) of a partition represented by
    208  * given GPT entry.
    209  */
    210 size_t GptGetEntrySizeLba(const GptEntry *e)
    211 {
    212 	return (e->ending_lba - e->starting_lba + 1);
    213 }
    214 
    215 /*
    216  * Func: GptGetEntrySize
    217  * Desc: This function returns size(in bytes) of a partition represented by
    218  * given GPT entry.
    219  */
    220 size_t GptGetEntrySizeBytes(const GptData *gpt, const GptEntry *e)
    221 {
    222 	return GptGetEntrySizeLba(e) * gpt->sector_bytes;
    223 }
    224