From ea8b02d230ce3f43040a0ec5d30fa5d203999386 Mon Sep 17 00:00:00 2001 From: Tom Marshall Date: Mon, 13 Apr 2015 16:11:50 -0700 Subject: [PATCH] Initial checkin --- Android.mk | 23 ++ Makefile | 91 ++++++ crc32.c | 114 +++++++ crc32.h | 8 + gpt.c | 735 ++++++++++++++++++++++++++++++++++++++++++++ gpt.h | 164 ++++++++++ gpted.c | 356 +++++++++++++++++++++ readline/history.h | 6 + readline/readline.c | 29 ++ readline/readline.h | 6 + types.h | 8 + util.c | 71 +++++ util.h | 13 + 13 files changed, 1624 insertions(+) create mode 100644 Android.mk create mode 100644 Makefile create mode 100644 crc32.c create mode 100644 crc32.h create mode 100644 gpt.c create mode 100644 gpt.h create mode 100644 gpted.c create mode 100644 readline/history.h create mode 100644 readline/readline.c create mode 100644 readline/readline.h create mode 100644 types.h create mode 100644 util.c create mode 100644 util.h diff --git a/Android.mk b/Android.mk new file mode 100644 index 0000000..8973a04 --- /dev/null +++ b/Android.mk @@ -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) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..cf8020f --- /dev/null +++ b/Makefile @@ -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); diff --git a/crc32.c b/crc32.c new file mode 100644 index 0000000..870931a --- /dev/null +++ b/crc32.c @@ -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 +#include + +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; +} diff --git a/crc32.h b/crc32.h new file mode 100644 index 0000000..e06922e --- /dev/null +++ b/crc32.h @@ -0,0 +1,8 @@ +#ifndef CRC32_H +#define CRC32_H + +#include + +uint32_t crc32(uint32_t crc, const void *buf, size_t len); + +#endif diff --git a/gpt.c b/gpt.c new file mode 100644 index 0000000..d5953c6 --- /dev/null +++ b/gpt.c @@ -0,0 +1,735 @@ +#define _LARGEFILE64_SOURCE + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#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; +} diff --git a/gpt.h b/gpt.h new file mode 100644 index 0000000..f72223e --- /dev/null +++ b/gpt.h @@ -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 diff --git a/gpted.c b/gpted.c new file mode 100644 index 0000000..1866afa --- /dev/null +++ b/gpted.c @@ -0,0 +1,356 @@ +#include +#include +#include + +#include +#include + +#include +#include + +#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 \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; +} diff --git a/readline/history.h b/readline/history.h new file mode 100644 index 0000000..e909ff6 --- /dev/null +++ b/readline/history.h @@ -0,0 +1,6 @@ +#ifndef HISTORY_H +#define HISTORY_H + +/* empty */ + +#endif diff --git a/readline/readline.c b/readline/readline.c new file mode 100644 index 0000000..201bea6 --- /dev/null +++ b/readline/readline.c @@ -0,0 +1,29 @@ +#include +#include +#include + +#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; +} diff --git a/readline/readline.h b/readline/readline.h new file mode 100644 index 0000000..c8489fd --- /dev/null +++ b/readline/readline.h @@ -0,0 +1,6 @@ +#ifndef READLINE_H +#define READLINE_H + +extern char *readline(const char *prompt); + +#endif diff --git a/types.h b/types.h new file mode 100644 index 0000000..60266c3 --- /dev/null +++ b/types.h @@ -0,0 +1,8 @@ +#ifndef TYPES_H +#define TYPES_H + +#include + +typedef unsigned char byte; + +#endif diff --git a/util.c b/util.c new file mode 100644 index 0000000..4ab6c4c --- /dev/null +++ b/util.c @@ -0,0 +1,71 @@ +#include +#include +#include +#include +#include + +#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; +} diff --git a/util.h b/util.h new file mode 100644 index 0000000..29d1f99 --- /dev/null +++ b/util.h @@ -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