Initial checkin
This commit is contained in:
parent
2ccb532c7c
commit
ea8b02d230
|
@ -0,0 +1,23 @@
|
|||
LOCAL_PATH:= $(call my-dir)
|
||||
|
||||
common_cflags := -Wall -Werror -DANDROID
|
||||
# XXX: nexus?
|
||||
ifeq ($(BOARD_USES_QCOM_HARDWARE),true)
|
||||
common_cflags += -DQCOM
|
||||
endif
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := libgpt
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_CFLAGS := $(common_cflags)
|
||||
LOCAL_SRC_FILES := util.c readline/readline.c crc32.c gpt.c
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
LOCAL_MODULE := gpted
|
||||
LOCAL_MODULE_TAGS := optional
|
||||
LOCAL_SRC_FILES := gpted.c
|
||||
LOCAL_CFLAGS := $(common_cflags)
|
||||
LOCAL_FORCE_STATIC_EXECUTABLE := true
|
||||
LOCAL_STATIC_LIBRARIES := libc libgpt
|
||||
include $(BUILD_EXECUTABLE)
|
|
@ -0,0 +1,91 @@
|
|||
# Makefile for gpted
|
||||
|
||||
PREFIX ?= /usr/local
|
||||
BINDIR ?= $(PREFIX)/bin
|
||||
MANDIR ?= $(PREFIX)/share/man
|
||||
|
||||
ifeq (,$(VERSION))
|
||||
VERSION := $(shell git rev-parse --short HEAD)
|
||||
ifneq (,$(shell git diff-index --name-only HEAD))
|
||||
VERSION := $(VERSION)-dirty
|
||||
endif
|
||||
endif
|
||||
|
||||
CC := gcc
|
||||
AR := ar
|
||||
LD := gcc
|
||||
CFLAGS := -g -Wall -Werror
|
||||
LDFLAGS := -g
|
||||
|
||||
LIB_C_SRC := util.c crc32.c gpt.c
|
||||
TARGET_LIB := libgpt.a
|
||||
|
||||
EXE_C_SRC := gpted.c
|
||||
TARGET_EXE := gpted
|
||||
|
||||
ifeq ($(WITH_READLINE),true)
|
||||
SYSLIBS := -lreadline
|
||||
else
|
||||
CFLAGS += -I.
|
||||
LIB_C_SRC += readline/readline.c
|
||||
endif
|
||||
|
||||
LIB_C_OBJ := $(LIB_C_SRC:.c=.o)
|
||||
EXE_C_OBJ := $(EXE_C_SRC:.c=.o)
|
||||
|
||||
C_SRC := $(LIB_C_SRC) $(EXE_C_SRC)
|
||||
C_OBJ := $(LIB_C_OBJ) $(EXE_C_OBJ)
|
||||
|
||||
all: $(TARGET_EXE)
|
||||
|
||||
define cmd_c_compile
|
||||
$(CC) $(CPPFLAGS) $(CFLAGS) -MD -MP -MF .$(@F:.o=.d) -c -o $@ $<
|
||||
endef
|
||||
-include $(wildcard .*.d)
|
||||
|
||||
%.o: %.c
|
||||
$(cmd_c_compile)
|
||||
|
||||
verdep := $(shell grep -l "^\#include \"version\.h\"" $(C_SRC))
|
||||
ifneq ($(verdep),)
|
||||
$(verdep): version.h
|
||||
endif
|
||||
|
||||
$(TARGET_EXE): $(EXE_C_OBJ) $(TARGET_LIB)
|
||||
$(LD) $(LDFLAGS) -o $@ $^ $(SYSLIBS)
|
||||
|
||||
$(TARGET_LIB): $(LIB_C_OBJ)
|
||||
$(AR) r $@ $^
|
||||
|
||||
version.h:
|
||||
@echo "/* Autogenerated file. Do not edit. */" > .$@.tmp && \
|
||||
echo "#ifndef VERSION_H" >> .$@.tmp && \
|
||||
echo "#define PACKAGE \"$(TARGET)\"" >> .$@.tmp && \
|
||||
echo "#define VERSION \"$(VERSION)\"" >> .$@.tmp && \
|
||||
echo "#endif" >> .$@.tmp && \
|
||||
rm -f $@ && \
|
||||
mv -f .$@.tmp $@
|
||||
|
||||
tags: $(C_SRC)
|
||||
ctags -R
|
||||
|
||||
.PHONY: install
|
||||
install -D gpted $(DESTDIR)$(BINDIR)
|
||||
install -D gpted.1 $(DESTDIR)$(MANDIR)/man1
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f core .*.d readline/*.o *.o *.a gpted
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
rm -f .*.d
|
||||
|
||||
.PHONY: tarball
|
||||
tarball: distclean
|
||||
(d=`basename $PWD`;
|
||||
cd ..; \
|
||||
cp -ar $d $d-$(VERSION); \
|
||||
tar czvf $d-$(VERSION).tar.gz `find $d-$(VERSION) -type f | egrep -v "CVS|debian|\.git"`; \
|
||||
rm -rf $d-$(VERSION); \
|
||||
cd $(PWD);
|
|
@ -0,0 +1,114 @@
|
|||
/*-
|
||||
* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or
|
||||
* code or tables extracted from it, as desired without restriction.
|
||||
*
|
||||
* First, the polynomial itself and its table of feedback terms. The
|
||||
* polynomial is
|
||||
* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
|
||||
*
|
||||
* Note that we take it "backwards" and put the highest-order term in
|
||||
* the lowest-order bit. The X^32 term is "implied"; the LSB is the
|
||||
* X^31 term, etc. The X^0 term (usually shown as "+1") results in
|
||||
* the MSB being 1
|
||||
*
|
||||
* Note that the usual hardware shift register implementation, which
|
||||
* is what we're using (we're merely optimizing it by doing eight-bit
|
||||
* chunks at a time) shifts bits into the lowest-order term. In our
|
||||
* implementation, that means shifting towards the right. Why do we
|
||||
* do it this way? Because the calculated CRC must be transmitted in
|
||||
* order from highest-order term to lowest-order term. UARTs transmit
|
||||
* characters in order from LSB to MSB. By storing the CRC this way
|
||||
* we hand it to the UART in the order low-byte to high-byte; the UART
|
||||
* sends each low-bit to hight-bit; and the result is transmission bit
|
||||
* by bit from highest- to lowest-order term without requiring any bit
|
||||
* shuffling on our part. Reception works similarly
|
||||
*
|
||||
* The feedback terms table consists of 256, 32-bit entries. Notes
|
||||
*
|
||||
* The table can be generated at runtime if desired; code to do so
|
||||
* is shown later. It might not be obvious, but the feedback
|
||||
* terms simply represent the results of eight shift/xor opera
|
||||
* tions for all combinations of data and CRC register values
|
||||
*
|
||||
* The values must be right-shifted by eight bits by the "updcrc
|
||||
* logic; the shift must be unsigned (bring in zeroes). On some
|
||||
* hardware you could probably optimize the shift in assembler by
|
||||
* using byte-swap instructions
|
||||
* polynomial $edb88320
|
||||
*
|
||||
*
|
||||
* CRC32 code derived from work by Gary S. Brown.
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static uint32_t crc32_tab[] = {
|
||||
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f,
|
||||
0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
|
||||
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, 0x1db71064, 0x6ab020f2,
|
||||
0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
|
||||
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9,
|
||||
0xfa0f3d63, 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
|
||||
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, 0x42b2986c,
|
||||
0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
|
||||
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423,
|
||||
0xcfba9599, 0xb8bda50f, 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
|
||||
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106,
|
||||
0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
|
||||
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d,
|
||||
0x91646c97, 0xe6635c01, 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
|
||||
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950,
|
||||
0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
|
||||
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7,
|
||||
0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
|
||||
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, 0x5005713c, 0x270241aa,
|
||||
0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
|
||||
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81,
|
||||
0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
|
||||
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, 0xe3630b12, 0x94643b84,
|
||||
0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
|
||||
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb,
|
||||
0x196c3671, 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
|
||||
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, 0xa1d1937e,
|
||||
0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
|
||||
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55,
|
||||
0x316e8eef, 0x4669be79, 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
|
||||
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28,
|
||||
0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
|
||||
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f,
|
||||
0x72076785, 0x05005713, 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
|
||||
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242,
|
||||
0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
|
||||
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69,
|
||||
0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
|
||||
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, 0xaed16a4a, 0xd9d65adc,
|
||||
0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
|
||||
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693,
|
||||
0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
|
||||
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d
|
||||
};
|
||||
|
||||
#define DO1(buf) crc = crc32_tab[((int)crc ^ (*buf++)) & 0xff] ^ (crc >> 8);
|
||||
#define DO2(buf) DO1(buf); DO1(buf);
|
||||
#define DO4(buf) DO2(buf); DO2(buf);
|
||||
#define DO8(buf) DO4(buf); DO4(buf);
|
||||
|
||||
uint32_t
|
||||
crc32(uint32_t crc, const void *ubuf, size_t len)
|
||||
{
|
||||
const uint8_t *buf = ubuf;
|
||||
|
||||
crc = crc ^ ~0U;
|
||||
|
||||
while (len >= 8) {
|
||||
DO8(buf);
|
||||
len -= 8;
|
||||
}
|
||||
while (len) {
|
||||
DO1(buf);
|
||||
len--;
|
||||
}
|
||||
|
||||
return crc ^ ~0U;
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef CRC32_H
|
||||
#define CRC32_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
uint32_t crc32(uint32_t crc, const void *buf, size_t len);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,735 @@
|
|||
#define _LARGEFILE64_SOURCE
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/mount.h>
|
||||
|
||||
#include "gpt.h"
|
||||
|
||||
#include "util.h"
|
||||
#include "crc32.h"
|
||||
|
||||
#define DEFAULT_LBSIZE 512
|
||||
#define MIN_LBSIZE 512
|
||||
#define MAX_LBSIZE 4096
|
||||
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
#define IS_PART_RO(gpt,n) ((n) <= (gpt->pad_idx))
|
||||
#else
|
||||
#define IS_PART_RO(gpt,n) (0)
|
||||
#endif
|
||||
|
||||
static const byte gpt_header_signature[8] = {
|
||||
0x45, 0x46, 0x49, 0x20, 0x50, 0x41, 0x52, 0x54
|
||||
};
|
||||
|
||||
static void u16_to_ascii(const byte *u, char *s)
|
||||
{
|
||||
uint32_t n;
|
||||
for (n = 0; n < 72/2; ++n) {
|
||||
s[n] = u[n*2];
|
||||
}
|
||||
s[72/2] = '\0';
|
||||
}
|
||||
|
||||
static void guid_to_ascii(const byte *guid, char *s)
|
||||
{
|
||||
uint32_t p1;
|
||||
uint16_t p2;
|
||||
uint16_t p3;
|
||||
unsigned char p4[8];
|
||||
|
||||
memcpy(&p1, guid + 0, 4);
|
||||
memcpy(&p2, guid + 4, 2);
|
||||
memcpy(&p3, guid + 6, 2);
|
||||
memcpy(p4, guid + 8, 8);
|
||||
|
||||
sprintf(s, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
|
||||
p1, p2, p3, p4[0], p4[1],
|
||||
p4[2], p4[3], p4[4], p4[5], p4[6], p4[7]);
|
||||
}
|
||||
|
||||
static void gpt_header_show(const char *msg, const struct gpt_header *header)
|
||||
{
|
||||
char guidstr[80];
|
||||
|
||||
guid_to_ascii(header->disk_guid, guidstr);
|
||||
|
||||
printf("%s:"
|
||||
" size=%lu\n"
|
||||
" current_lba=%llu\n"
|
||||
" backup_lba=%llu\n"
|
||||
" first_usable_lba=%llu\n"
|
||||
" last_usable_lba=%llu\n"
|
||||
" guid=%s\n"
|
||||
" ptbl_lba=%llu\n"
|
||||
" ptbl_count=%lu\n"
|
||||
" ptbl_entry_size=%lu\n",
|
||||
msg,
|
||||
(unsigned long)header->size,
|
||||
(unsigned long long)header->current_lba,
|
||||
(unsigned long long)header->backup_lba,
|
||||
(unsigned long long)header->first_usable_lba,
|
||||
(unsigned long long)header->last_usable_lba,
|
||||
guidstr,
|
||||
(unsigned long long)header->ptbl_lba,
|
||||
(unsigned long)header->ptbl_count,
|
||||
(unsigned long)header->ptbl_entry_size);
|
||||
}
|
||||
|
||||
static void gpt_part_show(uint32_t idx, const struct gpt_partition *part)
|
||||
{
|
||||
char name[72+1];
|
||||
uint64_t start, end, size;
|
||||
|
||||
u16_to_ascii(part->name, name);
|
||||
start = part->first_lba;
|
||||
end = part->last_lba;
|
||||
size = end - start + 1;
|
||||
printf(" p%-2u: [%8llu..%8llu] size=%8llu name=%s\n",
|
||||
idx,
|
||||
(unsigned long long)start,
|
||||
(unsigned long long)end,
|
||||
(unsigned long long)size,
|
||||
name);
|
||||
}
|
||||
|
||||
static int gpt_header_is_valid(struct gpt_header *header, uint32_t lbsize)
|
||||
{
|
||||
uint32_t read_crc, calc_crc;
|
||||
|
||||
if (memcmp(header->signature, gpt_header_signature, sizeof(gpt_header_signature)) != 0) {
|
||||
return -1;
|
||||
}
|
||||
if (header->size < GPT_HDR_SIZE || header->size > lbsize) {
|
||||
return -1;
|
||||
}
|
||||
if (header->revision != 0x00010000) {
|
||||
return -1;
|
||||
}
|
||||
read_crc = header->crc;
|
||||
header->crc = 0;
|
||||
calc_crc = crc32(0, header, header->size);
|
||||
if (read_crc != calc_crc) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header->ptbl_count < GPT_MIN_PARTITIONS || header->ptbl_count > GPT_MAX_PARTITIONS) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (header->ptbl_entry_size < GPT_PART_SIZE || header->ptbl_entry_size > MAX_LBSIZE) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_open(struct gpt *gpt, const char *dev)
|
||||
{
|
||||
int rc;
|
||||
off64_t off;
|
||||
byte buf[MAX_LBSIZE];
|
||||
uint32_t calc_crc;
|
||||
uint64_t next_lba;
|
||||
uint32_t n;
|
||||
struct stat st;
|
||||
int primary_header_valid = 0;
|
||||
int backup_header_valid = 0;
|
||||
|
||||
memset(gpt, 0, sizeof(struct gpt));
|
||||
|
||||
gpt->fd = open(dev, O_RDWR);
|
||||
if (gpt->fd < 0) {
|
||||
perror("open");
|
||||
return -1;
|
||||
}
|
||||
|
||||
rc = fstat(gpt->fd, &st);
|
||||
if (rc != 0) {
|
||||
perror("fstat");
|
||||
return -1;
|
||||
}
|
||||
|
||||
gpt->lbsize = DEFAULT_LBSIZE;
|
||||
if (S_ISBLK(st.st_mode)) {
|
||||
unsigned long blklen;
|
||||
/* XXX: Linux LB size is always 512? */
|
||||
if (ioctl(gpt->fd, BLKGETSIZE, &blklen, sizeof(blklen)) == 0) {
|
||||
gpt->lblen = blklen;
|
||||
}
|
||||
}
|
||||
|
||||
off = lseek64(gpt->fd, 1*gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
perror("lseek");
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
rc = read(gpt->fd, buf, gpt->lbsize);
|
||||
if (rc != (ssize_t)gpt->lbsize) {
|
||||
fprintf(stderr, "bad read\n");
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
memcpy(&gpt->header, buf, sizeof(struct gpt_header));
|
||||
if (gpt_header_is_valid(&gpt->header, gpt->lbsize) != 0) {
|
||||
fprintf(stderr, "bad gpt header\n");
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
if (gpt->header.current_lba != 1 ||
|
||||
gpt->header.backup_lba != gpt->lblen - 1 ||
|
||||
gpt->header.first_usable_lba != 34 ||
|
||||
gpt->header.last_usable_lba <= gpt->header.first_usable_lba ||
|
||||
gpt->header.last_usable_lba >= gpt->lblen - 1 ||
|
||||
gpt->header.ptbl_lba != 2 ||
|
||||
gpt->header.ptbl_count < GPT_MIN_PARTITIONS ||
|
||||
gpt->header.ptbl_count > GPT_MAX_PARTITIONS ||
|
||||
gpt->header.ptbl_entry_size > gpt->lbsize) {
|
||||
fprintf(stderr, "W: bad primary gpt\n");
|
||||
goto primary_header_out;
|
||||
}
|
||||
#else
|
||||
if (gpt->header.current_lba != 1 ||
|
||||
gpt->header.backup_lba >= gpt->lblen ||
|
||||
gpt->header.first_usable_lba < 2 ||
|
||||
gpt->header.first_usable_lba >= gpt->lblen ||
|
||||
gpt->header.last_usable_lba <= gpt->header.first_usable_lba ||
|
||||
gpt->header.last_usable_lba >= gpt->lblen ||
|
||||
gpt->header.ptbl_lba >= gpt->lblen ||
|
||||
gpt->header.ptbl_count < GPT_MIN_PARTITIONS ||
|
||||
gpt->header.ptbl_count > GPT_MAX_PARTITIONS ||
|
||||
gpt->header.ptbl_entry_size > gpt->lbsize) {
|
||||
fprintf(stderr, "W: bad primary gpt\n");
|
||||
goto primary_header_out;
|
||||
}
|
||||
#endif
|
||||
primary_header_valid = 1;
|
||||
primary_header_out:
|
||||
|
||||
/* Validate backup GPT for block devices */
|
||||
if (S_ISBLK(st.st_mode) &&
|
||||
gpt->header.backup_lba > 2 &&
|
||||
gpt->header.backup_lba < gpt->lblen) {
|
||||
off = lseek64(gpt->fd, gpt->header.backup_lba * gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
fprintf(stderr, "bad backup seek\n");
|
||||
goto backup_header_out;
|
||||
}
|
||||
rc = read(gpt->fd, buf, gpt->lbsize);
|
||||
if (rc != (ssize_t)gpt->lbsize) {
|
||||
fprintf(stderr, "bad backup read\n");
|
||||
goto backup_header_out;
|
||||
}
|
||||
memcpy(&gpt->backup_header, buf, sizeof(struct gpt_header));
|
||||
if (gpt_header_is_valid(&gpt->backup_header, gpt->lbsize) != 0) {
|
||||
fprintf(stderr, "bad backup header\n");
|
||||
goto backup_header_out;
|
||||
}
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
if (gpt->backup_header.current_lba != gpt->header.backup_lba ||
|
||||
gpt->backup_header.backup_lba != 1 ||
|
||||
gpt->backup_header.first_usable_lba != gpt->header.first_usable_lba ||
|
||||
gpt->backup_header.last_usable_lba != gpt->header.last_usable_lba ||
|
||||
memcmp(gpt->backup_header.disk_guid, gpt->header.disk_guid, 16) != 0 ||
|
||||
gpt->backup_header.ptbl_lba >= gpt->lblen ||
|
||||
gpt->backup_header.ptbl_count != gpt->header.ptbl_count ||
|
||||
gpt->backup_header.ptbl_entry_size != gpt->header.ptbl_entry_size) {
|
||||
fprintf(stderr, "W: bad backup gpt\n");
|
||||
goto backup_header_out;
|
||||
}
|
||||
#else
|
||||
if (gpt->backup_header.current_lba != gpt->header.backup_lba ||
|
||||
gpt->backup_header.backup_lba != 1 ||
|
||||
gpt->backup_header.first_usable_lba != gpt->header.first_usable_lba ||
|
||||
gpt->backup_header.last_usable_lba != gpt->header.last_usable_lba ||
|
||||
memcmp(gpt->backup_header.disk_guid, gpt->header.disk_guid, 16) != 0 ||
|
||||
gpt->backup_header.ptbl_lba >= gpt->lblen ||
|
||||
gpt->backup_header.ptbl_count != gpt->header.ptbl_count ||
|
||||
gpt->backup_header.ptbl_entry_size != gpt->header.ptbl_entry_size) {
|
||||
fprintf(stderr, "W: bad backup gpt\n");
|
||||
goto backup_header_out;
|
||||
}
|
||||
#endif
|
||||
backup_header_valid = 1;
|
||||
}
|
||||
backup_header_out:
|
||||
|
||||
next_lba = gpt->header.first_usable_lba;
|
||||
|
||||
if (primary_header_valid) {
|
||||
off = lseek64(gpt->fd, gpt->header.ptbl_lba * gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
perror("lseek\n");
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
calc_crc = 0;
|
||||
for (n = 0; n < gpt->header.ptbl_count; ++n) {
|
||||
gpt->partitions[n] = (struct gpt_partition *)malloc(sizeof(struct gpt_partition));
|
||||
memset(gpt->partitions[n], 0, sizeof(struct gpt_partition));
|
||||
rc = read(gpt->fd, gpt->partitions[n], gpt->header.ptbl_entry_size);
|
||||
if (rc < 0 || (uint32_t)rc != gpt->header.ptbl_entry_size) {
|
||||
fprintf(stderr, "failed to read partition entry %u\n", n);
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
calc_crc = crc32(calc_crc, gpt->partitions[n], gpt->header.ptbl_entry_size);
|
||||
if (gpt->partitions[n]->first_lba == 0 && gpt->partitions[n]->last_lba == 0) {
|
||||
continue;
|
||||
}
|
||||
if (gpt->partitions[n]->first_lba < next_lba ||
|
||||
gpt->partitions[n]->last_lba < gpt->partitions[n]->first_lba ||
|
||||
gpt->partitions[n]->last_lba > gpt->header.last_usable_lba) {
|
||||
fprintf(stderr, "bad lba in partition entry %u\n", n);
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
gpt->last_used_idx = n;
|
||||
}
|
||||
|
||||
if (gpt->header.ptbl_crc != calc_crc) {
|
||||
fprintf(stderr, "bad ptbl crc\n");
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (backup_header_valid) {
|
||||
int warned = 0;
|
||||
off = lseek64(gpt->fd, gpt->backup_header.ptbl_lba * gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
perror("lseek\n");
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
calc_crc = 0;
|
||||
for (n = 0; n < gpt->backup_header.ptbl_count; ++n) {
|
||||
struct gpt_partition backup_part;
|
||||
rc = read(gpt->fd, &backup_part, gpt->backup_header.ptbl_entry_size);
|
||||
if (rc < 0 || (uint32_t)rc != gpt->backup_header.ptbl_entry_size) {
|
||||
fprintf(stderr, "failed to read backup partition entry %u\n", n);
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
if (memcmp(gpt->partitions[n], &backup_part, sizeof(struct gpt_partition)) != 0) {
|
||||
if (!warned) {
|
||||
fprintf(stderr, "mismatched backup partition entry %u\n", n);
|
||||
warned = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
gpt->pad_idx = gpt_part_find(gpt, "pad");
|
||||
if (gpt->pad_idx == GPT_PART_INVALID) {
|
||||
fprintf(stderr, "no pad found\n");
|
||||
close(gpt->fd);
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_write(const struct gpt *gpt)
|
||||
{
|
||||
int rc;
|
||||
off64_t off;
|
||||
struct gpt_header hdr;
|
||||
uint32_t n;
|
||||
|
||||
memcpy(&hdr, &gpt->header, sizeof(hdr));
|
||||
|
||||
hdr.ptbl_crc = 0;
|
||||
for (n = 0; n < gpt->header.ptbl_count; ++n) {
|
||||
hdr.ptbl_crc = crc32(hdr.ptbl_crc, gpt->partitions[n], gpt->header.ptbl_entry_size);
|
||||
}
|
||||
|
||||
hdr.crc = 0;
|
||||
hdr.crc = crc32(0, &hdr, hdr.size);
|
||||
|
||||
off = lseek64(gpt->fd, hdr.current_lba * gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
perror("lseek");
|
||||
return -1;
|
||||
}
|
||||
rc = write(gpt->fd, &hdr, hdr.size);
|
||||
if (rc < 0 || (uint32_t)rc != hdr.size) {
|
||||
fprintf(stderr, "bad primary header write\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
off = lseek64(gpt->fd, hdr.ptbl_lba * gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
perror("lseek\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (n = 0; n < hdr.ptbl_count; ++n) {
|
||||
rc = write(gpt->fd, gpt->partitions[n], hdr.ptbl_entry_size);
|
||||
if (rc < 0 || (uint32_t)rc != hdr.ptbl_entry_size) {
|
||||
fprintf(stderr, "bad primary partition write\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
hdr.current_lba = gpt->header.backup_lba;
|
||||
hdr.backup_lba = 1;
|
||||
hdr.ptbl_lba = gpt->lblen -
|
||||
ROUNDUP(hdr.ptbl_count * hdr.ptbl_entry_size, gpt->lbsize) / gpt->lbsize - 1;
|
||||
|
||||
hdr.crc = 0;
|
||||
hdr.crc = crc32(0, &hdr, hdr.size);
|
||||
|
||||
off = lseek64(gpt->fd, hdr.current_lba * gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
perror("lseek");
|
||||
return -1;
|
||||
}
|
||||
rc = write(gpt->fd, &hdr, hdr.size);
|
||||
if (rc < 0 || (uint32_t)rc != hdr.size) {
|
||||
fprintf(stderr, "bad backup header write\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
off = lseek64(gpt->fd, hdr.ptbl_lba * gpt->lbsize, SEEK_SET);
|
||||
if (off < 0) {
|
||||
perror("lseek\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (n = 0; n < hdr.ptbl_count; ++n) {
|
||||
rc = write(gpt->fd, gpt->partitions[n], hdr.ptbl_entry_size);
|
||||
if (rc < 0 || (uint32_t)rc != hdr.ptbl_entry_size) {
|
||||
fprintf(stderr, "bad backup partition write\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_close(struct gpt *gpt)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = close(gpt->fd);
|
||||
gpt->fd = -1;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void gpt_show(const struct gpt *gpt)
|
||||
{
|
||||
uint32_t n;
|
||||
|
||||
gpt_header_show("Primary GPT", &gpt->header);
|
||||
|
||||
if (gpt->backup_header.size != 0) {
|
||||
gpt_header_show("Backup GPT", &gpt->backup_header);
|
||||
}
|
||||
|
||||
printf("Partition table: count=%u\n", (unsigned int)gpt->last_used_idx);
|
||||
for (n = 0; n < gpt->header.ptbl_count; ++n) {
|
||||
if (gpt->partitions[n]->first_lba == 0 && gpt->partitions[n]->last_lba == 0) {
|
||||
continue;
|
||||
}
|
||||
gpt_part_show(n, gpt->partitions[n]);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t gpt_part_find(const struct gpt *gpt, const char *name)
|
||||
{
|
||||
uint32_t n;
|
||||
char curname[72/2+1];
|
||||
|
||||
for (n = 0; n < gpt->header.ptbl_count; ++n) {
|
||||
u16_to_ascii(gpt->partitions[n]->name, curname);
|
||||
if (!strcmp(name, curname)) {
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
||||
return GPT_PART_INVALID;
|
||||
}
|
||||
|
||||
int gpt_part_name(const struct gpt *gpt, uint32_t idx, char *name)
|
||||
{
|
||||
u16_to_ascii(gpt->partitions[idx]->name, name);
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t gpt_part_size(const struct gpt *gpt, uint32_t idx)
|
||||
{
|
||||
uint64_t lbsize;
|
||||
|
||||
lbsize = gpt->partitions[idx]->last_lba - gpt->partitions[idx]->first_lba + 1;
|
||||
return (lbsize * gpt->lbsize);
|
||||
}
|
||||
|
||||
int gpt_part_add(struct gpt *gpt, uint32_t idx, const struct gpt_partition *part, int follow)
|
||||
{
|
||||
uint64_t lba_min, lba_max;
|
||||
uint32_t n;
|
||||
|
||||
if (IS_PART_RO(gpt, idx) || idx > gpt->last_used_idx+1 ||
|
||||
gpt->last_used_idx >= gpt->header.ptbl_count) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
lba_min = (idx == 0 ?
|
||||
gpt->header.first_usable_lba :
|
||||
gpt->partitions[idx-1]->last_lba + 1);
|
||||
lba_max = (idx == gpt->header.ptbl_count ?
|
||||
gpt->header.last_usable_lba :
|
||||
gpt->partitions[idx]->first_lba - 1);
|
||||
|
||||
if (part->first_lba < lba_min || part->last_lba > lba_max) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (n = gpt->header.ptbl_count; n > idx; --n) {
|
||||
gpt->partitions[n] = gpt->partitions[n-1];
|
||||
}
|
||||
gpt->partitions[n] = (struct gpt_partition *)malloc(sizeof(struct gpt_partition));
|
||||
memcpy(gpt->partitions[n], part, sizeof(struct gpt_partition));
|
||||
|
||||
gpt->last_used_idx++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_part_del(struct gpt *gpt, uint32_t idx, int follow)
|
||||
{
|
||||
uint32_t n;
|
||||
uint64_t len, delta;
|
||||
|
||||
if (IS_PART_RO(gpt, idx) || idx > gpt->last_used_idx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
len = gpt->partitions[idx]->last_lba - gpt->partitions[idx]->first_lba + 1;
|
||||
delta = 0 - len;
|
||||
|
||||
for (n = idx; n < gpt->last_used_idx; ++n) {
|
||||
gpt->partitions[n] = gpt->partitions[n+1];
|
||||
if (follow) {
|
||||
gpt->partitions[n]->first_lba += delta;
|
||||
gpt->partitions[n]->last_lba += delta;
|
||||
}
|
||||
}
|
||||
|
||||
gpt->last_used_idx--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_part_move(struct gpt *gpt, uint32_t idx, uint64_t lba, int follow)
|
||||
{
|
||||
uint32_t n;
|
||||
int64_t lbdelta;
|
||||
|
||||
if (IS_PART_RO(gpt, idx) || idx > gpt->last_used_idx) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
lbdelta = (int64_t)lba - (int64_t)gpt->partitions[idx]->first_lba;
|
||||
if (lbdelta == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lbdelta < 0) {
|
||||
/*
|
||||
* Figure out the minimum lba for the partition. For now, we will
|
||||
* just use the end of the previous partition. Later, we could
|
||||
* try to do something like "follow" in reverse.
|
||||
*/
|
||||
uint64_t lba_min = gpt->partitions[idx-1]->last_lba + 1;
|
||||
if (lba < lba_min) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Figure out the maximum lba delta:
|
||||
* - For the last partition, limit is last usable LBA less end LBA.
|
||||
* - If follow is set, limit is same as last partition.
|
||||
* - If follow is unset, limit is start of next partition less this partition size.
|
||||
*/
|
||||
uint64_t lbamaxdelta;
|
||||
if (idx == gpt->last_used_idx || follow) {
|
||||
uint32_t lui = gpt->last_used_idx;
|
||||
lbamaxdelta = gpt->header.last_usable_lba - gpt->partitions[lui]->last_lba;
|
||||
}
|
||||
else {
|
||||
lbamaxdelta = gpt->partitions[idx+1]->first_lba - gpt->partitions[idx]->last_lba - 1;
|
||||
}
|
||||
if (lbdelta > (int64_t)lbamaxdelta) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
gpt->partitions[idx]->first_lba += lbdelta;
|
||||
gpt->partitions[idx]->last_lba += lbdelta;
|
||||
|
||||
if (follow) {
|
||||
for (n = idx+1; n <= gpt->header.ptbl_count; ++n) {
|
||||
gpt->partitions[n]->first_lba += lbdelta;
|
||||
gpt->partitions[n]->last_lba += lbdelta;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_part_resize(struct gpt *gpt, uint32_t idx, uint64_t size, int follow)
|
||||
{
|
||||
uint32_t n;
|
||||
int64_t lbdelta;
|
||||
|
||||
if (IS_PART_RO(gpt, idx) || idx > gpt->last_used_idx) {
|
||||
return -1;
|
||||
}
|
||||
if (size & (gpt->lbsize - 1)) {
|
||||
return -1;
|
||||
}
|
||||
lbdelta = ((int64_t)size - (int64_t)gpt_part_size(gpt, idx)) / gpt->lbsize;
|
||||
if (lbdelta == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lbdelta > 0) {
|
||||
/*
|
||||
* Figure out how much the partition may expand:
|
||||
* - For the last partition, limit is last usable LBA.
|
||||
* - If follow is set, limit is same as last partition.
|
||||
* - If follow is unset, limit is start of next partition.
|
||||
*/
|
||||
uint64_t lbamaxdelta;
|
||||
if (idx == gpt->last_used_idx || follow) {
|
||||
uint32_t lui = gpt->last_used_idx;
|
||||
lbamaxdelta = gpt->header.last_usable_lba - gpt->partitions[lui]->last_lba;
|
||||
}
|
||||
else {
|
||||
lbamaxdelta = gpt->partitions[idx+1]->first_lba - gpt->partitions[idx]->last_lba - 1;
|
||||
}
|
||||
if (lbdelta > (int64_t)lbamaxdelta) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
gpt->partitions[idx]->last_lba += lbdelta;
|
||||
|
||||
if (follow) {
|
||||
for (n = idx+1; n <= gpt->last_used_idx; ++n) {
|
||||
gpt->partitions[n]->first_lba += lbdelta;
|
||||
gpt->partitions[n]->last_lba += lbdelta;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int gpt_part_save(struct gpt *gpt, uint32_t idx, const char *filename)
|
||||
{
|
||||
uint64_t remain;
|
||||
int fd;
|
||||
char buf[4096];
|
||||
|
||||
if (lseek64(gpt->fd,
|
||||
gpt->partitions[idx]->first_lba * gpt->lbsize,
|
||||
SEEK_SET) < 0) {
|
||||
return -1;
|
||||
}
|
||||
remain = gpt_part_size(gpt, idx);
|
||||
|
||||
fd = open(filename, O_WRONLY | O_CREAT, 0666);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
while (remain > 0) {
|
||||
size_t toread;
|
||||
ssize_t nread, nwritten;
|
||||
toread = (remain > sizeof(buf) ? sizeof(buf) : remain);
|
||||
nread = read(gpt->fd, buf, toread);
|
||||
if (nread <= 0) {
|
||||
goto out_err;
|
||||
}
|
||||
nwritten = write(fd, buf, nread);
|
||||
if (nwritten != nread) {
|
||||
goto out_err;
|
||||
}
|
||||
remain -= nread;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int gpt_part_load(struct gpt *gpt, uint32_t idx, const char *filename)
|
||||
{
|
||||
uint64_t remain;
|
||||
int fd;
|
||||
struct stat st;
|
||||
char buf[4096];
|
||||
|
||||
if (lseek64(gpt->fd,
|
||||
gpt->partitions[idx]->first_lba * gpt->lbsize,
|
||||
SEEK_SET) < 0) {
|
||||
return -1;
|
||||
}
|
||||
remain = gpt_part_size(gpt, idx);
|
||||
|
||||
fd = open(filename, O_RDONLY);
|
||||
if (fd < 0) {
|
||||
return -1;
|
||||
}
|
||||
if (fstat(fd, &st) != 0) {
|
||||
perror("fstat");
|
||||
goto out_err;
|
||||
}
|
||||
if ((uint64_t)st.st_size != gpt_part_size(gpt, idx)) {
|
||||
fprintf(stderr, "E: file %s has incorrect size\n", filename);
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
while (remain > 0) {
|
||||
size_t toread;
|
||||
ssize_t nread, nwritten;
|
||||
toread = (remain > sizeof(buf) ? sizeof(buf) : remain);
|
||||
nread = read(fd, buf, toread);
|
||||
if (nread <= 0) {
|
||||
goto out_err;
|
||||
}
|
||||
nwritten = write(gpt->fd, buf, nread);
|
||||
if (nwritten != nread) {
|
||||
goto out_err;
|
||||
}
|
||||
remain -= nread;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
return 0;
|
||||
|
||||
out_err:
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
#ifndef GPT_H
|
||||
#define GPT_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define GPT_HDR_SIZE 92
|
||||
struct gpt_header
|
||||
{
|
||||
byte signature[8];
|
||||
uint32_t revision;
|
||||
uint32_t size;
|
||||
uint32_t crc;
|
||||
uint32_t reserved;
|
||||
uint64_t current_lba;
|
||||
uint64_t backup_lba;
|
||||
uint64_t first_usable_lba;
|
||||
uint64_t last_usable_lba;
|
||||
byte disk_guid[16];
|
||||
uint64_t ptbl_lba;
|
||||
uint32_t ptbl_count;
|
||||
uint32_t ptbl_entry_size;
|
||||
uint32_t ptbl_crc;
|
||||
byte padding[512-GPT_HDR_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
#define GPT_PART_SIZE 128
|
||||
struct gpt_partition
|
||||
{
|
||||
byte type_guid[16];
|
||||
byte part_guid[16];
|
||||
uint64_t first_lba;
|
||||
uint64_t last_lba;
|
||||
byte flags[8];
|
||||
byte name[72];
|
||||
byte padding[512-GPT_PART_SIZE];
|
||||
} __attribute__((packed));
|
||||
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
#define GPT_MIN_PARTITIONS 8
|
||||
#define GPT_MAX_PARTITIONS 128
|
||||
#else
|
||||
#define GPT_MIN_PARTITIONS 4
|
||||
#define GPT_MAX_PARTITIONS 256
|
||||
#endif
|
||||
|
||||
#define GPT_PART_INVALID (uint32_t)(~0)
|
||||
|
||||
struct gpt
|
||||
{
|
||||
int fd;
|
||||
uint32_t lbsize;
|
||||
uint32_t lblen;
|
||||
struct gpt_header header;
|
||||
struct gpt_header backup_header;
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
uint32_t pad_idx;
|
||||
#endif
|
||||
uint32_t last_used_idx;
|
||||
struct gpt_partition* partitions[GPT_MAX_PARTITIONS];
|
||||
};
|
||||
|
||||
int gpt_open(struct gpt *gpt, const char *dev);
|
||||
int gpt_write(const struct gpt *gpt);
|
||||
int gpt_close(struct gpt *gpt);
|
||||
void gpt_show(const struct gpt *gpt);
|
||||
|
||||
/*
|
||||
* gpt_part_find: Find a partition
|
||||
* name: ASCII encoded name to find.
|
||||
* Returns index if found.
|
||||
* Returns GPT_PART_INVALID on error.
|
||||
*/
|
||||
uint32_t gpt_part_find(const struct gpt *gpt, const char *name);
|
||||
|
||||
#define GPT_PART_NAMELEN (72/2+1)
|
||||
int gpt_part_name(const struct gpt *gpt, uint32_t idx, char *name);
|
||||
uint64_t gpt_part_size(const struct gpt *gpt, uint32_t idx);
|
||||
|
||||
/*
|
||||
* gpt_part_add: Add a partititon
|
||||
* idx : Index of partition.
|
||||
* part : Partition data to add.
|
||||
* follow: Whether to follow/expand.
|
||||
*
|
||||
* If follow is zero, the partition at 'idx' must be empty, and there must be
|
||||
* enough room to insert it (either it must be the last entry, or the
|
||||
* following entry must start after the end of the new entry).
|
||||
*
|
||||
* If follow is nonzero, existing partitions starting at 'idx' are pushed up
|
||||
* to make room for the new partition. The total partition table must not
|
||||
* expand past the usable lba space.
|
||||
*
|
||||
* Returns zero if successful, nonzero on error.
|
||||
*/
|
||||
int gpt_part_add(struct gpt *gpt, uint32_t idx, const struct gpt_partition *part, int follow);
|
||||
|
||||
/*
|
||||
* gpt_part_del: Delete a partititon
|
||||
* idx : Index of partition.
|
||||
* follow: Whether to follow/contract.
|
||||
*
|
||||
* If follow is zero, the partition at 'idx' is zeroed.
|
||||
*
|
||||
* If follow is nonzero, existing partitions starting at 'idx' are pushed down
|
||||
* to fill in the unused space.
|
||||
*
|
||||
* Returns zero if successful, nonzero on error.
|
||||
*/
|
||||
int gpt_part_del(struct gpt *gpt, uint32_t idx, int follow);
|
||||
|
||||
/*
|
||||
* gpt_part_move: Move a partititon
|
||||
* idx : Index of partition.
|
||||
* lba : New starting LBA.
|
||||
* follow: Whether to follow.
|
||||
*
|
||||
* If follow is zero, the partition is moved and no other action is taken.
|
||||
* The destination must be within the current allowed space.
|
||||
*
|
||||
* If follow is nonzero, existing partitions starting at 'idx+1' are moved
|
||||
* in the same amount (up or down) as the specified partition. The
|
||||
* destination must be above the previous partititon and the total partition
|
||||
* table must not expand past the usable space.
|
||||
*
|
||||
* Returns zero if successful, nonzero on error.
|
||||
*/
|
||||
int gpt_part_move(struct gpt *gpt, uint32_t idx, uint64_t lba, int follow);
|
||||
|
||||
/*
|
||||
* gpt_part_resize: Resize a partititon
|
||||
* idx : Index of partition.
|
||||
* size : New size.
|
||||
* follow: Whether to follow.
|
||||
*
|
||||
* If follow is zero, the partition is resized and no other action is taken.
|
||||
* The destination must be within the current allowed space.
|
||||
*
|
||||
* If follow is nonzero, existing partitions starting at 'idx+1' are moved
|
||||
* in the same amount (up or down) as the specified partition delta. The
|
||||
* total partition table must not expand past the usable space.
|
||||
*
|
||||
* Returns zero if successful, nonzero on error.
|
||||
*/
|
||||
int gpt_part_resize(struct gpt *gpt, uint32_t idx, uint64_t size, int follow);
|
||||
|
||||
/*
|
||||
* gpt_part_save: Save a partition to a file
|
||||
* idx : Index of partition.
|
||||
* filename: Filename to save data.
|
||||
*
|
||||
* Returns zero if successful, nonzero on error.
|
||||
*/
|
||||
int gpt_part_save(struct gpt *gpt, uint32_t idx, const char *filename);
|
||||
|
||||
/*
|
||||
* gpt_part_load: Load a partition from a file
|
||||
* idx : Index of partition.
|
||||
* filename: Filename to load data.
|
||||
*
|
||||
* Returns zero if successful, nonzero on error.
|
||||
*/
|
||||
int gpt_part_load(struct gpt *gpt, uint32_t idx, const char *filename);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,356 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
#include "gpt.h"
|
||||
|
||||
#define MAX_ARGS 8
|
||||
|
||||
static int cmd_help(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_quit(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int cmd_show(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
gpt_show(gpt);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_write(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = gpt_write(gpt);
|
||||
if (rc != 0) {
|
||||
printf("E: write failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
static const char *non_firmware[] = {
|
||||
"recovery", "boot",
|
||||
"system", "userdata", "cache", "sdcard",
|
||||
/* persist? */
|
||||
NULL
|
||||
};
|
||||
|
||||
static int cmd_firmware_save(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int all = 0;
|
||||
uint32_t startidx, idx;
|
||||
|
||||
if (argc > 1) {
|
||||
all = !strcmp(argv[1], "all");
|
||||
}
|
||||
|
||||
startidx = (all ? 0 : gpt->pad_idx+1);
|
||||
for (idx = startidx; idx <= gpt->last_used_idx; ++idx) {
|
||||
char name[72/2+1];
|
||||
char filename[72/2+4+1];
|
||||
int skip = 0;
|
||||
const char **entry;
|
||||
|
||||
gpt_part_name(gpt, idx, name);
|
||||
for (entry = non_firmware; *entry; ++entry) {
|
||||
if (!strcmp(name, *entry)) {
|
||||
skip = 1;
|
||||
}
|
||||
}
|
||||
if (skip) {
|
||||
printf("Skip %s\n", name);
|
||||
}
|
||||
else {
|
||||
printf("Save %s\n", name);
|
||||
sprintf(filename, "%s.img", name);
|
||||
gpt_part_save(gpt, idx, filename);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_firmware_load(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int all = 0;
|
||||
uint32_t startidx, idx;
|
||||
|
||||
if (argc > 1) {
|
||||
all = !strcmp(argv[1], "all");
|
||||
}
|
||||
|
||||
startidx = (all ? 0 : gpt->pad_idx+1);
|
||||
for (idx = startidx; idx <= gpt->last_used_idx; ++idx) {
|
||||
char name[72/2+1];
|
||||
char filename[72/2+4+1];
|
||||
int skip = 0;
|
||||
const char **entry;
|
||||
|
||||
gpt_part_name(gpt, idx, name);
|
||||
for (entry = non_firmware; *entry; ++entry) {
|
||||
if (!strcmp(name, *entry)) {
|
||||
skip = 1;
|
||||
}
|
||||
}
|
||||
if (skip) {
|
||||
printf("Skip %s\n", name);
|
||||
}
|
||||
else {
|
||||
printf("Load %s\n", name);
|
||||
sprintf(filename, "%s.img", name);
|
||||
gpt_part_load(gpt, idx, filename);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int cmd_part_add(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
printf("E: not implemented\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_part_del(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int rc;
|
||||
uint32_t idx;
|
||||
int follow;
|
||||
|
||||
if (argc < 2) {
|
||||
printf("E: not enough args\n");
|
||||
return 0;
|
||||
}
|
||||
idx = gpt_part_find(gpt, argv[1]);
|
||||
if (idx == GPT_PART_INVALID) {
|
||||
printf("E: part %s not found\n", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
follow = (argc > 2 && !strcmp(argv[2], "follow"));
|
||||
rc = gpt_part_del(gpt, idx, follow);
|
||||
if (rc != 0) {
|
||||
printf("E: failed\n");
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_part_move(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int rc;
|
||||
uint32_t idx;
|
||||
uint64_t lb;
|
||||
int follow;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("E: not enough args\n");
|
||||
return 0;
|
||||
}
|
||||
idx = gpt_part_find(gpt, argv[1]);
|
||||
if (idx == GPT_PART_INVALID) {
|
||||
printf("E: part %s not found\n", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
lb = strtoull_u(argv[2], NULL, 0);
|
||||
follow = (argc > 3 && !strcmp(argv[3], "follow"));
|
||||
rc = gpt_part_move(gpt, idx, lb, follow);
|
||||
if (rc != 0) {
|
||||
printf("E: failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_part_resize(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int rc;
|
||||
uint32_t idx;
|
||||
uint64_t size;
|
||||
int follow;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("E: not enough args\n");
|
||||
return 0;
|
||||
}
|
||||
idx = gpt_part_find(gpt, argv[1]);
|
||||
if (idx == GPT_PART_INVALID) {
|
||||
printf("E: part %s not found\n", argv[1]);
|
||||
return 0;
|
||||
}
|
||||
follow = (argc > 3 && !strcmp(argv[3], "follow"));
|
||||
if (!strcmp(argv[2], "max")) {
|
||||
if (follow) {
|
||||
struct gpt_partition *endpart;
|
||||
endpart = gpt->partitions[gpt->last_used_idx];
|
||||
size = gpt_part_size(gpt, idx) +
|
||||
(gpt->header.last_usable_lba - endpart->last_lba) * gpt->lbsize;
|
||||
}
|
||||
else {
|
||||
size = strtoull_u(argv[2], NULL, 0);
|
||||
}
|
||||
}
|
||||
else {
|
||||
size = strtoull_u(argv[2], NULL, 0);
|
||||
}
|
||||
rc = gpt_part_resize(gpt, idx, size, follow);
|
||||
if (rc != 0) {
|
||||
printf("E: failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_part_load(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int rc;
|
||||
uint32_t idx;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("E: not enough args\n");
|
||||
return 0;
|
||||
}
|
||||
idx = gpt_part_find(gpt, argv[1]);
|
||||
rc = gpt_part_load(gpt, idx, argv[2]);
|
||||
if (rc != 0) {
|
||||
printf("E: failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cmd_part_save(struct gpt *gpt, unsigned int argc, const char **argv)
|
||||
{
|
||||
int rc;
|
||||
uint32_t idx;
|
||||
|
||||
if (argc < 3) {
|
||||
printf("E: not enough args\n");
|
||||
return 0;
|
||||
}
|
||||
idx = gpt_part_find(gpt, argv[1]);
|
||||
rc = gpt_part_save(gpt, idx, argv[2]);
|
||||
if (rc != 0) {
|
||||
printf("E: failed\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
struct dispatch_entry
|
||||
{
|
||||
const char *cmd;
|
||||
int (*func)(struct gpt *gpt, unsigned int argc, const char **argv);
|
||||
};
|
||||
|
||||
static struct dispatch_entry dispatch_table[] = {
|
||||
{ "help", cmd_help },
|
||||
{ "quit", cmd_quit },
|
||||
{ "show", cmd_show },
|
||||
{ "write", cmd_write },
|
||||
|
||||
#if defined(ANDROID) && defined(QCOM)
|
||||
{ "firmware-save", cmd_firmware_save },
|
||||
{ "firmware-load", cmd_firmware_load },
|
||||
#endif
|
||||
|
||||
{ "part-add", cmd_part_add },
|
||||
{ "part-del", cmd_part_del },
|
||||
{ "part-move", cmd_part_move },
|
||||
{ "part-resize", cmd_part_resize },
|
||||
{ "part-load", cmd_part_load },
|
||||
{ "part-save", cmd_part_save },
|
||||
|
||||
{ NULL, NULL }
|
||||
};
|
||||
|
||||
static int dispatch(struct gpt *gpt, char *line)
|
||||
{
|
||||
char *p;
|
||||
unsigned int argc;
|
||||
const char *argv[MAX_ARGS];
|
||||
struct dispatch_entry *entry;
|
||||
|
||||
while (*line == ' ') {
|
||||
++line;
|
||||
}
|
||||
if (*line == '#' || *line == ';') {
|
||||
return 0;
|
||||
}
|
||||
|
||||
argc = 0;
|
||||
p = strtok(line, " ");
|
||||
while (p != NULL) {
|
||||
argv[argc++] = p;
|
||||
p = strtok(NULL, " ");
|
||||
}
|
||||
if (argc == 0) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (entry = dispatch_table; entry->cmd != NULL; ++entry) {
|
||||
if (!strcmp(argv[0], entry->cmd)) {
|
||||
return entry->func(gpt, argc, argv);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Unknown command %s\n", argv[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
int rc;
|
||||
struct gpt gpt;
|
||||
const char* dev;
|
||||
const char *prompt;
|
||||
char *line;
|
||||
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s <device>\n", argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
dev = argv[1];
|
||||
rc = gpt_open(&gpt, dev);
|
||||
if (rc != 0) {
|
||||
fprintf(stderr, "Failed to read gpt\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
prompt = (isatty(STDIN_FILENO) ? "partedit> " : NULL);
|
||||
while ((line = readline(prompt)) != NULL) {
|
||||
rc = dispatch(&gpt, line);
|
||||
free(line);
|
||||
if (rc != 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prompt) {
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
gpt_close(&gpt);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef HISTORY_H
|
||||
#define HISTORY_H
|
||||
|
||||
/* empty */
|
||||
|
||||
#endif
|
|
@ -0,0 +1,29 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "readline.h"
|
||||
|
||||
char *readline(const char *prompt)
|
||||
{
|
||||
char *buf = NULL;
|
||||
size_t n = 0;
|
||||
ssize_t len;
|
||||
|
||||
if (prompt && *prompt) {
|
||||
fprintf(stdout, "%s", prompt);
|
||||
fflush(stdout);
|
||||
}
|
||||
len = getline(&buf, &n, stdin);
|
||||
if (len == -1) {
|
||||
free(buf);
|
||||
buf = NULL;
|
||||
}
|
||||
else {
|
||||
char *p = strchr(buf, '\n');
|
||||
if (p)
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
#ifndef READLINE_H
|
||||
#define READLINE_H
|
||||
|
||||
extern char *readline(const char *prompt);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,8 @@
|
|||
#ifndef TYPES_H
|
||||
#define TYPES_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef unsigned char byte;
|
||||
|
||||
#endif
|
|
@ -0,0 +1,71 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "util.h"
|
||||
|
||||
void hexdump(const byte *buf, uint32_t len)
|
||||
{
|
||||
uint32_t x, y;
|
||||
for (y = 0; y*16 < len; ++y) {
|
||||
for (x = 0; x < 16 && y*16+x < len; ++x) {
|
||||
printf("%02x ", buf[y*16+x]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int user_multiplier(char **p)
|
||||
{
|
||||
char c = **p;
|
||||
switch (c)
|
||||
{
|
||||
case 's': case 'S':
|
||||
++(*p);
|
||||
return 512;
|
||||
case 'k': case 'K':
|
||||
++(*p);
|
||||
return 1024;
|
||||
case 'm': case 'M':
|
||||
++(*p);
|
||||
return 1024*1024;
|
||||
case 'g': case 'G':
|
||||
++(*p);
|
||||
return 1024*1024*1024;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
unsigned long int strtoul_u(const char *p, char **endp, int base)
|
||||
{
|
||||
char *u_endp;
|
||||
unsigned long int val;
|
||||
unsigned int mul;
|
||||
val = strtoul(p, &u_endp, base);
|
||||
if (val == ULONG_MAX || errno == ERANGE) {
|
||||
return val;
|
||||
}
|
||||
mul = user_multiplier(&u_endp);
|
||||
if (endp)
|
||||
*endp = u_endp;
|
||||
return val * mul;
|
||||
}
|
||||
|
||||
unsigned long long int strtoull_u(const char *p, char **endp, int base)
|
||||
{
|
||||
char *u_endp;
|
||||
unsigned long long int val;
|
||||
unsigned int mul;
|
||||
val = strtoull(p, &u_endp, base);
|
||||
if (val == ULLONG_MAX || errno == ERANGE) {
|
||||
return val;
|
||||
}
|
||||
mul = user_multiplier(&u_endp);
|
||||
if (endp)
|
||||
*endp = u_endp;
|
||||
return val * mul;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#include "types.h"
|
||||
|
||||
#define ROUNDUP(x,n) ((((x) + (n)-1) / (n)) * (n))
|
||||
|
||||
void hexdump(const byte *buf, uint32_t len);
|
||||
|
||||
unsigned long int strtoul_u(const char *p, char **endp, int base);
|
||||
unsigned long long int strtoull_u(const char *p, char **endp, int base);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue