Initial checkin

This commit is contained in:
Tom Marshall 2015-04-13 16:11:50 -07:00
parent 2ccb532c7c
commit ea8b02d230
13 changed files with 1624 additions and 0 deletions

23
Android.mk Normal file
View File

@ -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)

91
Makefile Normal file
View File

@ -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);

114
crc32.c Normal file
View File

@ -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;
}

8
crc32.h Normal file
View File

@ -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

735
gpt.c Normal file
View File

@ -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;
}

164
gpt.h Normal file
View File

@ -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

356
gpted.c Normal file
View File

@ -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;
}

6
readline/history.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef HISTORY_H
#define HISTORY_H
/* empty */
#endif

29
readline/readline.c Normal file
View File

@ -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;
}

6
readline/readline.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef READLINE_H
#define READLINE_H
extern char *readline(const char *prompt);
#endif

8
types.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef TYPES_H
#define TYPES_H
#include <stdint.h>
typedef unsigned char byte;
#endif

71
util.c Normal file
View File

@ -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;
}

13
util.h Normal file
View File

@ -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