diff --git a/Android.mk b/Android.mk index 60c2487..2afb46f 100644 --- a/Android.mk +++ b/Android.mk @@ -49,7 +49,7 @@ LOCAL_STATIC_LIBRARIES += libamend include $(BUILD_EXECUTABLE) include $(commands_recovery_local_path)/amend/Android.mk -include $(commands_recovery_local_path)/dump_image/Android.mk +include $(commands_recovery_local_path)/nandroid/Android.mk include $(commands_recovery_local_path)/minui/Android.mk include $(commands_recovery_local_path)/minzip/Android.mk include $(commands_recovery_local_path)/mtdutils/Android.mk diff --git a/dump_image/Android.mk b/dump_image/Android.mk deleted file mode 100644 index ab8ac38..0000000 --- a/dump_image/Android.mk +++ /dev/null @@ -1,15 +0,0 @@ -ifneq ($(TARGET_SIMULATOR),true) -ifeq ($(TARGET_ARCH),arm) - -LOCAL_PATH := $(call my-dir) -include $(CLEAR_VARS) -LOCAL_SRC_FILES := dump_image.c mtdutils.c ../mtdutils/mounts.c -LOCAL_MODULE := recovery_dump_image -LOCAL_MODULE_TAGS := eng -LOCAL_STATIC_LIBRARIES := libcutils libc -LOCAL_MODULE_STEM := dump_image -LOCAL_FORCE_STATIC_EXECUTABLE := true -include $(BUILD_EXECUTABLE) - -endif # TARGET_ARCH == arm -endif # !TARGET_SIMULATOR diff --git a/dump_image/mtdutils.c b/dump_image/mtdutils.c deleted file mode 100644 index 9ee7246..0000000 --- a/dump_image/mtdutils.c +++ /dev/null @@ -1,600 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include // for _IOW, _IOR, mount() -#include -#include -#undef NDEBUG -#include - -#include "mtdutils.h" - -struct MtdPartition { - int device_index; - unsigned int size; - unsigned int erase_size; - char *name; -}; - -struct MtdReadContext { - const MtdPartition *partition; - char *buffer; - size_t read_size; - size_t consumed; - int fd; -}; - -struct MtdWriteContext { - const MtdPartition *partition; - char *buffer; - size_t stored; - int fd; -}; - -typedef struct { - MtdPartition *partitions; - int partitions_allocd; - int partition_count; -} MtdState; - -static MtdState g_mtd_state = { - NULL, // partitions - 0, // partitions_allocd - -1 // partition_count -}; - -#define MTD_PROC_FILENAME "/proc/mtd" - -int -mtd_scan_partitions() -{ - char buf[2048]; - const char *bufp; - int fd; - int i; - ssize_t nbytes; - - if (g_mtd_state.partitions == NULL) { - const int nump = 32; - MtdPartition *partitions = malloc(nump * sizeof(*partitions)); - if (partitions == NULL) { - errno = ENOMEM; - return -1; - } - g_mtd_state.partitions = partitions; - g_mtd_state.partitions_allocd = nump; - memset(partitions, 0, nump * sizeof(*partitions)); - } - g_mtd_state.partition_count = 0; - - /* Initialize all of the entries to make things easier later. - * (Lets us handle sparsely-numbered partitions, which - * may not even be possible.) - */ - for (i = 0; i < g_mtd_state.partitions_allocd; i++) { - MtdPartition *p = &g_mtd_state.partitions[i]; - if (p->name != NULL) { - free(p->name); - p->name = NULL; - } - p->device_index = -1; - } - - /* Open and read the file contents. - */ - fd = open(MTD_PROC_FILENAME, O_RDONLY); - if (fd < 0) { - goto bail; - } - nbytes = read(fd, buf, sizeof(buf) - 1); - close(fd); - if (nbytes < 0) { - goto bail; - } - buf[nbytes] = '\0'; - - /* Parse the contents of the file, which looks like: - * - * # cat /proc/mtd - * dev: size erasesize name - * mtd0: 00080000 00020000 "bootloader" - * mtd1: 00400000 00020000 "mfg_and_gsm" - * mtd2: 00400000 00020000 "0000000c" - * mtd3: 00200000 00020000 "0000000d" - * mtd4: 04000000 00020000 "system" - * mtd5: 03280000 00020000 "userdata" - */ - bufp = buf; - while (nbytes > 0) { - int mtdnum, mtdsize, mtderasesize; - int matches; - char mtdname[64]; - mtdname[0] = '\0'; - mtdnum = -1; - - matches = sscanf(bufp, "mtd%d: %x %x \"%63[^\"]", - &mtdnum, &mtdsize, &mtderasesize, mtdname); - /* This will fail on the first line, which just contains - * column headers. - */ - if (matches == 4) { - MtdPartition *p = &g_mtd_state.partitions[mtdnum]; - p->device_index = mtdnum; - p->size = mtdsize; - p->erase_size = mtderasesize; - p->name = strdup(mtdname); - if (p->name == NULL) { - errno = ENOMEM; - goto bail; - } - g_mtd_state.partition_count++; - } - - /* Eat the line. - */ - while (nbytes > 0 && *bufp != '\n') { - bufp++; - nbytes--; - } - if (nbytes > 0) { - bufp++; - nbytes--; - } - } - - return g_mtd_state.partition_count; - -bail: - // keep "partitions" around so we can free the names on a rescan. - g_mtd_state.partition_count = -1; - return -1; -} - -const MtdPartition * -mtd_find_partition_by_name(const char *name) -{ - if (g_mtd_state.partitions != NULL) { - int i; - for (i = 0; i < g_mtd_state.partitions_allocd; i++) { - MtdPartition *p = &g_mtd_state.partitions[i]; - if (p->device_index >= 0 && p->name != NULL) { - if (strcmp(p->name, name) == 0) { - return p; - } - } - } - } - return NULL; -} - -int -mtd_mount_partition(const MtdPartition *partition, const char *mount_point, - const char *filesystem, int read_only) -{ - const unsigned long flags = MS_NOATIME | MS_NODEV | MS_NODIRATIME; - char devname[64]; - int rv = -1; - - sprintf(devname, "/dev/block/mtdblock%d", partition->device_index); - if (!read_only) { - rv = mount(devname, mount_point, filesystem, flags, NULL); - } - if (read_only || rv < 0) { - rv = mount(devname, mount_point, filesystem, flags | MS_RDONLY, 0); - if (rv < 0) { - printf("Failed to mount %s on %s: %s\n", - devname, mount_point, strerror(errno)); - } else { - printf("Mount %s on %s read-only\n", devname, mount_point); - } - } -#if 1 //TODO: figure out why this is happening; remove include of stat.h - if (rv >= 0) { - /* For some reason, the x bits sometimes aren't set on the root - * of mounted volumes. - */ - struct stat st; - rv = stat(mount_point, &st); - if (rv < 0) { - return rv; - } - mode_t new_mode = st.st_mode | S_IXUSR | S_IXGRP | S_IXOTH; - if (new_mode != st.st_mode) { -printf("Fixing execute permissions for %s\n", mount_point); - rv = chmod(mount_point, new_mode); - if (rv < 0) { - printf("Couldn't fix permissions for %s: %s\n", - mount_point, strerror(errno)); - } - } - } -#endif - return rv; -} - -int -mtd_partition_info(const MtdPartition *partition, - size_t *total_size, size_t *erase_size, size_t *write_size) -{ - char mtddevname[32]; - sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); - int fd = open(mtddevname, O_RDONLY); - if (fd < 0) return -1; - - struct mtd_info_user mtd_info; - int ret = ioctl(fd, MEMGETINFO, &mtd_info); - close(fd); - if (ret < 0) return -1; - - if (total_size != NULL) *total_size = mtd_info.size; - if (erase_size != NULL) *erase_size = mtd_info.erasesize; - if (write_size != NULL) *write_size = mtd_info.writesize; - return 0; -} - -MtdReadContext *mtd_read_partition(const MtdPartition *partition) -{ - MtdReadContext *ctx = (MtdReadContext*) malloc(sizeof(MtdReadContext)); - if (ctx == NULL) return NULL; - - ctx->buffer = malloc(partition->erase_size); - if (ctx->buffer == NULL) { - free(ctx); - return NULL; - } - - char mtddevname[32]; - sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); - ctx->fd = open(mtddevname, O_RDONLY); - if (ctx->fd < 0) { - free(ctx); - free(ctx->buffer); - return NULL; - } - - ctx->partition = partition; - ctx->read_size = partition->erase_size; - ctx->consumed = ctx->read_size; - return ctx; -} - -static int read_block(const MtdReadContext *ctx, char *data) -{ - struct mtd_ecc_stats before, after; - off_t pos; - ssize_t size; - - if (ioctl(ctx->fd, ECCGETSTATS, &before)) { - fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); - return -1; - } - - pos = lseek(ctx->fd, 0, SEEK_CUR); - size = ctx->read_size; - - while (pos + size <= (int) ctx->partition->size) { - if (lseek(ctx->fd, pos, SEEK_SET) != pos || read(ctx->fd, data, size) != size) { - fprintf(stderr, "mtd: read error at 0x%08lx (%s)\n", - pos, strerror(errno)); - } else if (ioctl(ctx->fd, ECCGETSTATS, &after)) { - fprintf(stderr, "mtd: ECCGETSTATS error (%s)\n", strerror(errno)); - return -1; - } else if (after.failed != before.failed) { - fprintf(stderr, "mtd: ECC errors (%d soft, %d hard) at 0x%08lx\n", - after.corrected - before.corrected, - after.failed - before.failed, pos); - /* - * Reset error counts, so next read may succeed. - */ - before = after; - } else { - return 0; // Success! - } - - pos += ctx->read_size; - } - - errno = ENOSPC; - return -1; -} - -ssize_t mtd_read_data(MtdReadContext *ctx, char *data, size_t len) -{ - ssize_t read = 0; - while (read < (int) len) { - if (ctx->consumed < ctx->read_size) { - size_t avail = ctx->read_size - ctx->consumed; - size_t copy = len - read < avail ? len - read : avail; - memcpy(data + read, ctx->buffer + ctx->consumed, copy); - ctx->consumed += copy; - read += copy; - } - - // Read complete blocks directly into the user's buffer - while (ctx->consumed == ctx->read_size && - len - read >= ctx->read_size) { - if (read_block(ctx, data + read)) return -1; - read += ctx->read_size; - } - - // Read the next block into the buffer - if (ctx->consumed == ctx->read_size && read < (int) len) { - if (read_block(ctx, ctx->buffer)) return -1; - ctx->consumed = 0; - } - } - - return read; -} - -ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len) -{ - static const int SPARE_SIZE = (2048 >> 5); - struct mtd_info_user mtd_info; - struct mtd_oob_buf oob_buf; - struct nand_ecclayout ecc_layout; - struct nand_oobfree *fp; - unsigned char ecc[SPARE_SIZE]; - char *src, *dst; - int i, n, ret; - -/* - * FIXME: These two ioctls should be cached in MtdReadContext. - */ - ret = ioctl(ctx->fd, MEMGETINFO, &mtd_info); - if (ret < 0) - return -1; - - ret = ioctl(ctx->fd, ECCGETLAYOUT, &ecc_layout); - if (ret < 0) - return -1; - - ctx->read_size = mtd_info.writesize; - ctx->consumed = ctx->read_size; - -/* - * Read next good data block. - */ - ret = read_block(ctx, data); - if (ret < 0) - return -1; - - dst = src = data + ctx->read_size; - -/* - * Read OOB data for last block read in read_block(). - */ - oob_buf.start = lseek(ctx->fd, 0, SEEK_CUR) - ctx->read_size; - oob_buf.length = mtd_info.oobsize; - oob_buf.ptr = (unsigned char *) src; - - ret = ioctl(ctx->fd, MEMREADOOB, &oob_buf); - if (ret < 0) - return -1; - -/* - * As yaffs and yaffs2 use mode MEM_OOB_AUTO, but mtdchar uses - * MEM_OOB_PLACE, copy the spare data down the hard way. - * - * Safe away ECC data: - */ - for (i = 0; i < ecc_layout.eccbytes; i++) { - ecc[i] = src[ecc_layout.eccpos[i]]; - } - for ( ; i < SPARE_SIZE; i++) { - ecc[i] = 0; - } - -/* - * Copy yaffs2 spare data down. - */ - n = ecc_layout.oobavail; - fp = &ecc_layout.oobfree[0]; - while (n) { - if (fp->offset) { - memmove(dst, src + fp->offset, fp->length); - } - dst += fp->length; - n -= fp->length; - ++fp; - } - -/* - * Restore ECC data behind spare data. - */ - memcpy(dst, ecc, (ctx->read_size >> 5) - ecc_layout.oobavail); - - return ctx->read_size + (ctx->read_size >> 5); -} - -void mtd_read_close(MtdReadContext *ctx) -{ - close(ctx->fd); - free(ctx->buffer); - free(ctx); -} - -MtdWriteContext *mtd_write_partition(const MtdPartition *partition) -{ - MtdWriteContext *ctx = (MtdWriteContext*) malloc(sizeof(MtdWriteContext)); - if (ctx == NULL) return NULL; - - ctx->buffer = malloc(partition->erase_size); - if (ctx->buffer == NULL) { - free(ctx); - return NULL; - } - - char mtddevname[32]; - sprintf(mtddevname, "/dev/mtd/mtd%d", partition->device_index); - ctx->fd = open(mtddevname, O_RDWR); - if (ctx->fd < 0) { - free(ctx->buffer); - free(ctx); - return NULL; - } - - ctx->partition = partition; - ctx->stored = 0; - return ctx; -} - -static int write_block(const MtdPartition *partition, int fd, const char *data) -{ - off_t pos = lseek(fd, 0, SEEK_CUR); - if (pos == (off_t) -1) return 1; - - ssize_t size = partition->erase_size; - while (pos + size <= (int) partition->size) { - loff_t bpos = pos; - if (ioctl(fd, MEMGETBADBLOCK, &bpos) > 0) { - fprintf(stderr, "mtd: not writing bad block at 0x%08lx\n", pos); - pos += partition->erase_size; - continue; // Don't try to erase known factory-bad blocks. - } - - struct erase_info_user erase_info; - erase_info.start = pos; - erase_info.length = size; - int retry; - for (retry = 0; retry < 2; ++retry) { - if (ioctl(fd, MEMERASE, &erase_info) < 0) { - fprintf(stderr, "mtd: erase failure at 0x%08lx (%s)\n", - pos, strerror(errno)); - continue; - } - if (lseek(fd, pos, SEEK_SET) != pos || - write(fd, data, size) != size) { - fprintf(stderr, "mtd: write error at 0x%08lx (%s)\n", - pos, strerror(errno)); - } - - char verify[size]; - if (lseek(fd, pos, SEEK_SET) != pos || - read(fd, verify, size) != size) { - fprintf(stderr, "mtd: re-read error at 0x%08lx (%s)\n", - pos, strerror(errno)); - continue; - } - if (memcmp(data, verify, size) != 0) { - fprintf(stderr, "mtd: verification error at 0x%08lx (%s)\n", - pos, strerror(errno)); - continue; - } - - if (retry > 0) { - fprintf(stderr, "mtd: wrote block after %d retries\n", retry); - } - return 0; // Success! - } - - // Try to erase it once more as we give up on this block - fprintf(stderr, "mtd: skipping write block at 0x%08lx\n", pos); - ioctl(fd, MEMERASE, &erase_info); - pos += partition->erase_size; - } - - // Ran out of space on the device - errno = ENOSPC; - return -1; -} - -ssize_t mtd_write_data(MtdWriteContext *ctx, const char *data, size_t len) -{ - size_t wrote = 0; - while (wrote < len) { - // Coalesce partial writes into complete blocks - if (ctx->stored > 0 || len - wrote < ctx->partition->erase_size) { - size_t avail = ctx->partition->erase_size - ctx->stored; - size_t copy = len - wrote < avail ? len - wrote : avail; - memcpy(ctx->buffer + ctx->stored, data + wrote, copy); - ctx->stored += copy; - wrote += copy; - } - - // If a complete block was accumulated, write it - if (ctx->stored == ctx->partition->erase_size) { - if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; - ctx->stored = 0; - } - - // Write complete blocks directly from the user's buffer - while (ctx->stored == 0 && len - wrote >= ctx->partition->erase_size) { - if (write_block(ctx->partition, ctx->fd, data + wrote)) return -1; - wrote += ctx->partition->erase_size; - } - } - - return wrote; -} - -off_t mtd_erase_blocks(MtdWriteContext *ctx, int blocks) -{ - // Zero-pad and write any pending data to get us to a block boundary - if (ctx->stored > 0) { - size_t zero = ctx->partition->erase_size - ctx->stored; - memset(ctx->buffer + ctx->stored, 0, zero); - if (write_block(ctx->partition, ctx->fd, ctx->buffer)) return -1; - ctx->stored = 0; - } - - off_t pos = lseek(ctx->fd, 0, SEEK_CUR); - if ((off_t) pos == (off_t) -1) return pos; - - const int total = (ctx->partition->size - pos) / ctx->partition->erase_size; - if (blocks < 0) blocks = total; - if (blocks > total) { - errno = ENOSPC; - return -1; - } - - // Erase the specified number of blocks - while (blocks-- > 0) { - loff_t bpos = pos; - if (ioctl(ctx->fd, MEMGETBADBLOCK, &bpos) > 0) { - fprintf(stderr, "mtd: not erasing bad block at 0x%08lx\n", pos); - pos += ctx->partition->erase_size; - continue; // Don't try to erase known factory-bad blocks. - } - - struct erase_info_user erase_info; - erase_info.start = pos; - erase_info.length = ctx->partition->erase_size; - if (ioctl(ctx->fd, MEMERASE, &erase_info) < 0) { - fprintf(stderr, "mtd: erase failure at 0x%08lx\n", pos); - } - pos += ctx->partition->erase_size; - } - - return pos; -} - -int mtd_write_close(MtdWriteContext *ctx) -{ - int r = 0; - // Make sure any pending data gets written - if (mtd_erase_blocks(ctx, 0) == (off_t) -1) r = -1; - if (close(ctx->fd)) r = -1; - free(ctx->buffer); - free(ctx); - return r; -} diff --git a/dump_image/mtdutils.h b/dump_image/mtdutils.h deleted file mode 100644 index 4a35543..0000000 --- a/dump_image/mtdutils.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MTDUTILS_H_ -#define MTDUTILS_H_ - -#include // for size_t, etc. - -typedef struct MtdPartition MtdPartition; - -int mtd_scan_partitions(void); - -const MtdPartition *mtd_find_partition_by_name(const char *name); - -/* mount_point is like "/system" - * filesystem is like "yaffs2" - */ -int mtd_mount_partition(const MtdPartition *partition, const char *mount_point, - const char *filesystem, int read_only); - -/* get the partition and the minimum erase/write block size. NULL is ok. - */ -int mtd_partition_info(const MtdPartition *partition, - size_t *total_size, size_t *erase_size, size_t *write_size); - -/* read or write raw data from a partition, starting at the beginning. - * skips bad blocks as best we can. - */ -typedef struct MtdReadContext MtdReadContext; -typedef struct MtdWriteContext MtdWriteContext; - -MtdReadContext *mtd_read_partition(const MtdPartition *); -ssize_t mtd_read_data(MtdReadContext *, char *data, size_t data_len); -ssize_t mtd_read_raw(MtdReadContext *, char *data, size_t data_len); -void mtd_read_close(MtdReadContext *); - -MtdWriteContext *mtd_write_partition(const MtdPartition *); -ssize_t mtd_write_data(MtdWriteContext *, const char *data, size_t data_len); -off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ -int mtd_write_close(MtdWriteContext *); - -#endif // MTDUTILS_H_ diff --git a/mtdutils/Android.mk b/mtdutils/Android.mk index c96c702..b6a5ae4 100644 --- a/mtdutils/Android.mk +++ b/mtdutils/Android.mk @@ -27,6 +27,25 @@ LOCAL_MODULE_TAGS := eng LOCAL_STATIC_LIBRARIES := libmtdutils libcutils libc LOCAL_MODULE_STEM := flash_image LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_flash_image +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_MODULE := recovery_dump_image +LOCAL_MODULE_TAGS := eng +LOCAL_STATIC_LIBRARIES := libcutils libc +LOCAL_MODULE_STEM := dump_image +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_dump_image +include $(BUILD_EXECUTABLE) + +include $(CLEAR_VARS) +LOCAL_SRC_FILES := dump_image.c mtdutils.c mounts.c +LOCAL_MODULE := dump_image +LOCAL_MODULE_TAGS := eng include $(BUILD_EXECUTABLE) endif # TARGET_ARCH == arm diff --git a/dump_image/dump_image.c b/mtdutils/dump_image.c similarity index 100% rename from dump_image/dump_image.c rename to mtdutils/dump_image.c diff --git a/mtdutils/mtdutils.c b/mtdutils/mtdutils.c index 18e6a5d..25feabd 100644 --- a/mtdutils/mtdutils.c +++ b/mtdutils/mtdutils.c @@ -38,6 +38,7 @@ struct MtdPartition { struct MtdReadContext { const MtdPartition *partition; char *buffer; + size_t read_size; size_t consumed; int fd; }; @@ -564,3 +565,84 @@ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos) { } return pos; } + + +ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len) +{ + static const int SPARE_SIZE = (2048 >> 5); + struct mtd_info_user mtd_info; + struct mtd_oob_buf oob_buf; + struct nand_ecclayout ecc_layout; + struct nand_oobfree *fp; + unsigned char ecc[SPARE_SIZE]; + char *src, *dst; + int i, n, ret; + +/* + * FIXME: These two ioctls should be cached in MtdReadContext. + */ + ret = ioctl(ctx->fd, MEMGETINFO, &mtd_info); + if (ret < 0) + return -1; + + ret = ioctl(ctx->fd, ECCGETLAYOUT, &ecc_layout); + if (ret < 0) + return -1; + + ctx->read_size = mtd_info.writesize; + ctx->consumed = ctx->read_size; + +/* + * Read next good data block. + */ + ret = read_block(ctx->partition, ctx->fd, data); + if (ret < 0) + return -1; + + dst = src = data + ctx->read_size; + +/* + * Read OOB data for last block read in read_block(). + */ + oob_buf.start = lseek(ctx->fd, 0, SEEK_CUR) - ctx->read_size; + oob_buf.length = mtd_info.oobsize; + oob_buf.ptr = (unsigned char *) src; + + ret = ioctl(ctx->fd, MEMREADOOB, &oob_buf); + if (ret < 0) + return -1; + +/* + * As yaffs and yaffs2 use mode MEM_OOB_AUTO, but mtdchar uses + * MEM_OOB_PLACE, copy the spare data down the hard way. + * + * Safe away ECC data: + */ + for (i = 0; i < ecc_layout.eccbytes; i++) { + ecc[i] = src[ecc_layout.eccpos[i]]; + } + for ( ; i < SPARE_SIZE; i++) { + ecc[i] = 0; + } + +/* + * Copy yaffs2 spare data down. + */ + n = ecc_layout.oobavail; + fp = &ecc_layout.oobfree[0]; + while (n) { + if (fp->offset) { + memmove(dst, src + fp->offset, fp->length); + } + dst += fp->length; + n -= fp->length; + ++fp; + } + +/* + * Restore ECC data behind spare data. + */ + memcpy(dst, ecc, (ctx->read_size >> 5) - ecc_layout.oobavail); + + return ctx->read_size + (ctx->read_size >> 5); +} diff --git a/mtdutils/mtdutils.h b/mtdutils/mtdutils.h index 528a5bb..bd0ce17 100644 --- a/mtdutils/mtdutils.h +++ b/mtdutils/mtdutils.h @@ -52,4 +52,6 @@ off_t mtd_erase_blocks(MtdWriteContext *, int blocks); /* 0 ok, -1 for all */ off_t mtd_find_write_start(MtdWriteContext *ctx, off_t pos); int mtd_write_close(MtdWriteContext *); +ssize_t mtd_read_raw(MtdReadContext *ctx, char *data, size_t len); + #endif // MTDUTILS_H_ diff --git a/nandroid/Android.mk b/nandroid/Android.mk new file mode 100644 index 0000000..ae2e7fd --- /dev/null +++ b/nandroid/Android.mk @@ -0,0 +1,24 @@ +ifneq ($(TARGET_SIMULATOR),true) +ifeq ($(TARGET_ARCH),arm) + +LOCAL_PATH := $(call my-dir) +include $(CLEAR_VARS) +LOCAL_MODULE := recovery_nandroid +LOCAL_MODULE_CLASS := EXECUTABLES +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +LOCAL_SRC_FILES := nandroid-mobile.sh +LOCAL_MODULE_STEM := nandroid-mobile.sh +ADDITIONAL_RECOVERY_EXECUTABLES += recovery_nandroid +include $(BUILD_PREBUILT) + +include $(CLEAR_VARS) +LOCAL_MODULE := recovery_unyaffs +LOCAL_MODULE_STEM := unyaffs +LOCAL_FORCE_STATIC_EXECUTABLE := true +LOCAL_SRC_FILES := unyaffs.c +LOCAL_STATIC_LIBRARIES := libc libcutils +LOCAL_MODULE_PATH := $(TARGET_RECOVERY_ROOT_OUT)/sbin +include $(BUILD_EXECUTABLE) + +endif # TARGET_ARCH == arm +endif # !TARGET_SIMULATOR diff --git a/nandroid/nandroid-mobile.sh b/nandroid/nandroid-mobile.sh new file mode 100755 index 0000000..b5cae3d --- /dev/null +++ b/nandroid/nandroid-mobile.sh @@ -0,0 +1,1790 @@ +#!/sbin/sh + +# nandroid v2.2.2 - an Android backup tool for the G1 by infernix and brainaid +# restore capability added by cyanogen + +# pensive modified to allow to add prefixes to backups, and to restore specific backups +# pensive added the ability to exclude various images from the restore/backup operations, allows to preserve the newer +# recovery image if an older backup is being restored or to preserve user data. Also, saves space by not backing up +# partitions which change rarely. +# pensive added compressing backups and restoring compressed backups +# pensive added fetching system updates directly from the web into /sdcard/update.zip +# pensive added fetching system updates directly from the web into /cache and applying it. +# pensive added moving *update*.zip from /sdcard/download where a browser puts it to /sdcard/update.zip +# pensive added deletion of stale backups +# pensive added backup for ext2 partition on the sdcard to switch roms +# pensive added composite options --save NAME and --switchto NAME to switch ROMS +# pensive added list backup anywhere on the sdcard +# pensive added list updates (more precisely *.zip) anywhere on the sdcard +# Amon_RA : ext restore -> added check if ext backup is existing +# Amon_RA : ext restore -> added check if ext parition is existing + +# Requirements: + +# - a modded android in recovery mode (JF 1.3 will work by default) +# - adb shell as root in recovery mode if not using a pre-made recovery image +# - busybox in recovery mode +# - dump_image-arm-uclibc compiled and in path on phone +# - mkyaffs2image-arm-uclibc compiled and installed in path on phone +# - flash_image-arm-uclibc compiled and in path on phone +# - unyaffs-arm-uclibc compiled and in path on phone +# - for [de]compression needs gzip or bzip2, part of the busybox +# - wget for the wireless updates + +# Reference data: + +# dev: size erasesize name +#mtd0: 00040000 00020000 "misc" +#mtd1: 00500000 00020000 "recovery" +#mtd2: 00280000 00020000 "boot" +#mtd3: 04380000 00020000 "system" +#mtd4: 04380000 00020000 "cache" +#mtd5: 04ac0000 00020000 "userdata" +#mtd6 is everything, dump splash1 with: dd if=/dev/mtd/mtd6ro of=/sdcard/splash1.img skip=19072 bs=2048 count=150 + +# We don't dump misc or cache because they do not contain any useful data that we are aware of at this time. + +# Logical steps (v2.2.1): +# +# 0. test for a target dir and the various tools needed, if not found then exit with error. +# 1. check "adb devices" for a device in recovery mode. set DEVICEID variable to the device ID. abort when not found. +# 2. mount system and data partitions read-only, set up adb portforward and create destdir +# 3. check free space on /cache, exit if less blocks than 20MB free +# 4. push required tools to device in /cache +# 5 for partitions boot recovery misc: +# 5a get md5sum for content of current partition *on the device* (no data transfered) +# 5b while MD5sum comparison is incorrect (always is the first time): +# 5b1 dump current partition to a netcat session +# 5b2 start local netcat to dump image to current dir +# 5b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) +# 6 for partitions system data: +# 6a get md5sum for tar of content of current partition *on the device* (no data transfered) +# 6b while MD5sum comparison is incorrect (always is the first time): +# 6b1 tar current partition to a netcat session +# 6b2 start local netcat to dump tar to current dir +# 6b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) +# 6c if i'm running as root: +# 6c1 create a temp dir using either tempdir command or the deviceid in /tmp +# 6c2 extract tar to tempdir +# 6c3 invoke mkyaffs2image to create the img +# 6c4 clean up +# 7. remove tools from device /cache +# 8. umount system and data on device +# 9. print success. + + +DEVICEID=foo +RECOVERY=foo + +SUBNAME="" +NORECOVERY=0 +NOBOOT=0 +NODATA=0 +NOSYSTEM=0 +NOMISC=0 +NOCACHE=0 +NOSPLASH1=0 +NOSPLASH2=0 +EXT2=0 + +COMPRESS=0 +GETUPDATE=0 +RESTORE=0 +BACKUP=0 +DELETE=0 +WEBGET=0 +LISTBACKUP=0 +LISTUPDATE=0 +AUTOREBOOT=0 +AUTOAPPLY=0 +ITSANUPDATE=0 +ITSANIMAGE=0 +WEBGETSOURCE="" +WEBGETTARGET="/sdcard" + +DEFAULTUPDATEPATH="/sdcard/download" + +DEFAULTWEBUPDATE=http://n0rp.chemlab.org/android/update-cm-3.6.8.1-signed.zip +# There really should be a clone link always pointing to the latest +#DEFAULTWEBUPDATE="http://n0rp.chemlab.org/android/latestupdate-signed.zip" +DEFAULTWEBIMAGE=http://n0rp.chemlab.org/android/cm-recovery-1.4-signed.zip + +# Set up the default (for pensive at least) nameservers +NAMESERVER1=64.46.128.3 +NAMESERVER2=64.46.128.4 + +# WiFi works, rmnet0 setup ??? +# Do not know how to start the rmnet0 interface in recovery +# If in normal mode "ifconfig rmnet0 down" kills rild too +# /system/bin/rild& exits immediately, todo? + + +DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` + +# This is the default repository for backups +BACKUPPATH="/sdcard/nandroid/$DEVICEID" + + +# Boot, Cache, Data, Ext2, Misc, Recovery, System, Splash1, Splash2 +# BACKUPLEGEND, If all the partitions are backed up it should be "CBDEMRS12" +# Enables the user to figure at a glance what is in the backup +BACKUPLEGEND="" + +# Normally we want tar to be verbose for confidence building. +TARFLAGS="v" + +DEFAULTCOMPRESSOR=gzip +DEFAULTEXT=.gz +DEFAULTLEVEL="-1" + +ASSUMEDEFAULTUSERINPUT=0 + +echo2log() +{ + if [ -e /cache/recovery/log ]; then + echo $1 >>/cache/recovery/log + else + echo $1 >/cache/recovery/log + fi +} + +batteryAtLeast() +{ + REQUIREDLEVEL=$1 + ENERGY=`cat /sys/class/power_supply/battery/capacity` + if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then + ENERGY=100 + fi + if [ ! $ENERGY -ge $REQUIREDLEVEL ]; then + $ECHO "Error: not enough battery power, need at least $REQUIREDLEVEL%." + $ECHO "Connect charger or USB power and try again" + exit 1 + fi +} + +if [ "`echo $0 | grep /sbin/nandroid-mobile.sh`" == "" ]; then + cp $0 /sbin + chmod 755 /sbin/`basename $0` + exec /sbin/`basename $0` $@ +fi + + +# Hm, have to handle old options for the current UI +case $1 in + restore) + shift + RESTORE=1 + ;; + backup) + shift + BACKUP=1 + ;; + --) + ;; +esac + +ECHO=echo +OUTPUT="" + +for option in $(getopt --name="nandroid-mobile v2.2.2" -l norecovery -l noboot -l nodata -l nosystem -l nocache -l nomisc -l nosplash1 -l nosplash2 -l subname: -l backup -l restore -l compress -l getupdate -l delete -l path -l webget: -l webgettarget: -l nameserver: -l nameserver2: -l bzip2: -l defaultinput -l autoreboot -l autoapplyupdate -l ext2 -l save: -l switchto: -l listbackup -l listupdate -l silent -l quiet -l help -- "cbruds:p:eql" "$@"); do + case $option in + --silent) + ECHO=echo2log + ASSUMEDEFAULTUSERINPUT=1 + TARFLAGS="" + OUTPUT=>>/cache/recovery/log + shift + ;; + --quiet) + ECHO=echo2log + ASSUMEDEFAULTUSERINPUT=1 + TARFLAGS="" + OUTPUT=>>/cache/recovery/log + shift + ;; + -q) + ECHO=echo2log + ASSUMEDEFAULTUSERINPUT=1 + TARFLAGS="" + OUTPUT=>>/cache/recovery/log + shift + ;; + --help) + ECHO=echo + $ECHO "Usage: $0 {--backup|--restore|--getupdate|--delete|--compress|--bzip2:ARG|--webget:URL|--listbackup|--listupdate} [options]" + $ECHO "" + $ECHO "--help Display this help" + $ECHO "" + $ECHO "-s | --subname: SUBSTRING In case of --backup the SUBSTRING is" + $ECHO " the prefix used with backup name" + $ECHO " in case of --restore or -c|--compress|--bzip2 or" + $ECHO " --delete SUBSTRING specifies backups on which to" + $ECHO " operate" + $ECHO "" + $ECHO "-u | --getupdate Will search /sdcard/download for files named" + $ECHO " *update*.zip, will prompt the user" + $ECHO " to narrow the choice if more than one is found," + $ECHO " and then move the latest, if not unique," + $ECHO " to sdcard root /sdcard with the update.zip name" + $ECHO " It is assumed the browser was used to put the *.zip" + $ECHO " in the /sdcard/download folder. -p|--path option" + $ECHO " would override /sdcard/download with the path of your" + $ECHO " choosing." + $ECHO "" + $ECHO "-p | --path DIR Requires an ARGUMENT, which is the path to where " + $ECHO " the backups are stored, can be used" + $ECHO " when the default path /sdcard/nandroid/$DEVICEID " + $ECHO " needs to be changed" + $ECHO "" + $ECHO "-b | --backup Will store a full system backup on $BACKUPPATH" + $ECHO " One can suppress backup of any image however with options" + $ECHO " starting with --no[partionname]" + $ECHO "" + $ECHO "-e | --ext2 Preserve the contents of the ext2 partition along with" + $ECHO " the other partitions being backed up, to easily switch roms." + $ECHO "" + $ECHO "-r | --restore Will restore the last made backup which matches --subname" + $ECHO " ARGUMENT for boot, system, recovery and data" + $ECHO " unless suppressed by other options" + $ECHO " if no --subname is supplied and the user fails to" + $ECHO " provide any input then the latest backup is used" + $ECHO " When restoring compressed backups, the images will remain" + $ECHO " decompressed after the restore, you need to use -c|-compress" + $ECHO " or --bzip2 to compress the backup again" + $ECHO "" + $ECHO "-d | --delete Will remove backups whose names match --subname ARGUMENT" + $ECHO " Will allow to narrow down, will ask if the user is certain." + $ECHO " Removes one backup at a time, repeat to remove multiple backups" + $ECHO "" + $ECHO "-c | --compress Will operate on chosen backups to compress image files," + $ECHO " resulting in saving of about 40MB out of 90+mb," + $ECHO " i.e. up to 20 backups can be stored in 1 GIG, if this " + $ECHO " option is turned on with --backup, the resulting backup will" + $ECHO " be compressed, no effect if restoring since restore will" + $ECHO " automatically uncompress compressed images if space is available" + $ECHO "" + $ECHO "--bzip2: -# Turns on -c|--compress and uses bzip2 for compression instead" + $ECHO " of gzip, requires an ARG -[1-9] compression level" + $ECHO "" + $ECHO "--webget: URL|\"\" Requires an argument, a valid URL for an *update*.zip file" + $ECHO " If a null string is passed then the default URL is used" + $ECHO " Will also create an update.MD5sum file and update.name with the" + $ECHO " original file name." + $ECHO "" + $ECHO "--nameserver: IP addr Provide the first nameserver IP address, to override the default" + $ECHO "" + $ECHO "--nameserver2: IP addr Provide the second nameserver IP address, to override the default" + $ECHO "" + $ECHO "--webgettarget: DIR Target directory to deposit the fetched update, default is" + $ECHO " /sdcard" + $ECHO "" + $ECHO "--norecovery Will suppress restore/backup of the recovery partition" + $ECHO " If recovery.img was not part of the backup, no need to use this" + $ECHO " option as the nandroid will not attempt to restore it, same goes" + $ECHO " for all the options below" + $ECHO "" + $ECHO "--noboot Will suppress restore/backup of the boot partition" + $ECHO "" + $ECHO "--nodata Will suppress restore/backup of the data partition" + $ECHO "" + $ECHO "--nosystem Will suppress restore/backup of the system partition" + $ECHO "" + $ECHO "--nocache Will suppress restore/backup of the cache partition" + $ECHO "" + $ECHO "--nomisc Will suppress restore/backup of the misc partition" + $ECHO "" + $ECHO "--nosplash1 Will suppress restore/backup of the splash1 partition" + $ECHO "" + $ECHO "--nosplash2 Will suppress restore/backup of the splash2 partition" + $ECHO "" + $ECHO "--defaultinput Makes nandroid-mobile non-interactive, assumes default" + $ECHO " inputs from the user." + $ECHO "" + $ECHO "--autoreboot Automatically reboot the phone after a backup, especially" + $ECHO " useful when the compression options are on -c|--compress| " + $ECHO " --bzip2 -level since the compression op takes time and" + $ECHO " you may want to go to sleep or do something else, and" + $ECHO " when a backup is over you want the calls and mms coming" + $ECHO " through, right?" + $ECHO "" + $ECHO "--autoapplyupdate Once the specific update is downloaded or chosen from the" + $ECHO " sdcard, apply it immediately. This option is valid as a " + $ECHO " modifier for either --webget or --getupdate options." + $ECHO "" + $ECHO "-e|--ext2 Save the contents of ext2 partition in the backup folder too." + $ECHO " Enables to keep different sets of apps for different ROMS and" + $ECHO " switch easily between them." + $ECHO "" + $ECHO "--save: ROMTAG Preserve EVERYTHING under ROMTAG name, a composite option," + $ECHO " it uses several other options." + $ECHO "" + $ECHO "--switchto: ROMTAG Restores your entire environment including app2sd apps, cache" + $ECHO " to the ROM environment named ROMTAG." + $ECHO "" + $ECHO "-q|--quiet|--silent Direct all the output to the recovery log, and assume default" + $ECHO " user inputs." + $ECHO "" + $ECHO "-l|--listbackup Will search the entire SDCARD for backup folders and will dump" + $ECHO " the list to stdout for use by the UI. Should be run with --silent" + $ECHO "" + $ECHO "--listupdate Will search the entire SDCARD for updates and will dump" + $ECHO " the list to stdout for use by the UI. Should be run with --silent" + exit 0 + ;; + --norecovery) + NORECOVERY=1 + #$ECHO "No recovery" + shift + ;; + --noboot) + NOBOOT=1 + #$ECHO "No boot" + shift + ;; + --nodata) + NODATA=1 + #$ECHO "No data" + shift + ;; + --nosystem) + NOSYSTEM=1 + #$ECHO "No system" + shift + ;; + --nocache) + NOCACHE=1 + #$ECHO "No cache" + shift + ;; + --nomisc) + NOMISC=1 + #$ECHO "No misc" + shift + ;; + --nosplash1) + NOSPLASH1=1 + #$ECHO "No splash1" + shift + ;; + --nosplash2) + NOSPLASH2=1 + #$ECHO "No splash2" + shift + ;; + --backup) + BACKUP=1 + #$ECHO "backup" + shift + ;; + -b) + BACKUP=1 + #$ECHO "backup" + shift + ;; + -e) + EXT2=1 + shift + ;; + --ext2) + EXT2=1 + shift + ;; + --restore) + RESTORE=1 + #$ECHO "restore" + shift + ;; + -r) + RESTORE=1 + #$ECHO "restore" + shift + ;; + --compress) + COMPRESS=1 + #$ECHO "Compress" + shift + ;; + -c) + COMPRESS=1 + #$ECHO "Compress" + shift + ;; + --bzip2) + COMPRESS=1 + DEFAULTCOMPRESSOR=bzip2 + DEFAULTEXT=.bz2 + if [ "$2" == "$option" ]; then + shift + fi + DEFAULTLEVEL="$2" + shift + ;; + --getupdate) + GETUPDATE=1 + shift + ;; + -u) + GETUPDATE=1 + shift + ;; + --subname) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + SUBNAME="$2" + shift + ;; + -s) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + SUBNAME="$2" + shift + ;; + --path) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + BACKUPPATH="$2" + DEFAULTUPDATEPATH="$2" + shift 2 + ;; + -p) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO $2 + BACKUPPATH="$2" + shift 2 + ;; + --delete) + DELETE=1 + shift + ;; + -d) + DELETE=1 + shift + ;; + --webgettarget) + if [ "$2" == "$option" ]; then + shift + fi + WEBGETTARGET="$2" + shift + ;; + --webget) + if [ "$2" == "$option" ]; then + shift + fi + #$ECHO "WEBGET" + # if the argument is "" stick with the default: /sdcard + if [ ! "$2" == "" ]; then + WEBGETSOURCE="$2" + fi + WEBGET=1 + shift + ;; + --nameserver) + if [ "$2" == "$option" ]; then + shift + fi + NAMESERVER1="$2" + shift + ;; + --nameserver2) + if [ "$2" == "$option" ]; then + shift + fi + NAMESERVER2="$2" + shift + ;; + --defaultinput) + ASSUMEDEFAULTUSERINPUT=1 + shift + ;; + --autoreboot) + AUTOREBOOT=1 + shift + ;; + --autoapplyupdate) + AUTOAPPLY=1 + shift + ;; + --save) + if [ "$2" == "$option" ]; then + shift + fi + SUBNAME="$2" + BACKUP=1 + ASSUMEDEFAULTUSERINPUT=1 + EXT2=1 + COMPRESS=1 + shift + ;; + --switchto) + if [ "$2" == "$option" ]; then + shift + fi + SUBNAME="$2" + RESTORE=1 + ASSUMEDEFAULTUSERINPUT=1 + EXT2=1 + shift + ;; + -l) + shift + LISTBACKUP=1 + ;; + --listbackup) + shift + LISTBACKUP=1 + ;; + --listupdate) + shift + LISTUPDATE=1 + ;; + --) + shift + break + ;; + esac +done + +$ECHO "" +$ECHO "nandroid-mobile v2.2.1" +$ECHO "" + +let OPS=$BACKUP +let OPS=$OPS+$RESTORE +let OPS=$OPS+$DELETE +let OPS=$OPS+$WEBGET +let OPS=$OPS+$GETUPDATE +let OPS=$OPS+$LISTBACKUP +let OPS=$OPS+$LISTUPDATE + +if [ "$OPS" -ge 2 ]; then + ECHO=echo + $ECHO "--backup, --restore, --delete, --webget, --getupdate, --listbackup, --listupdate are mutually exclusive operations." + $ECHO "Please, choose one and only one option!" + $ECHO "Aborting." + exit 1 +fi + +let OPS=$OPS+$COMPRESS + +if [ "$OPS" -lt 1 ]; then + ECHO=echo + $ECHO "Usage: $0 {-b|--backup|-r|--restore|-d|--delete|-u|--getupdate|--webget|-c|--compress|--bzip2 -level|-l|--listbackup|--listupdate} [options]" + $ECHO "At least one operation must be defined, try $0 --help for more information." + exit 0 +fi + + +if [ "$LISTBACKUP" == 1 ]; then + umount /sdcard 2>/dev/null + mount /sdcard 2>/dev/null + CHECK=`mount | grep /sdcard` + if [ "$CHECK" == "" ]; then + echo "Error: sdcard not mounted, aborting." + exit 1 + else + find /sdcard | grep nandroid.md5 | sed s/.gz//g | sed s/.bz2//g | sed s/nandroid.md5//g + umount /sdcard 2>/dev/null + exit 0 + fi +fi + +if [ "$LISTUPDATE" == 1 ]; then + umount /sdcard 2>/dev/null + mount /sdcard 2>/dev/null + CHECK=`mount | grep /sdcard` + if [ "$CHECK" == "" ]; then + echo "Error: sdcard not mounted, aborting." + exit 1 + else + find /sdcard | grep .zip + umount /sdcard 2>/dev/null + exit 0 + fi +fi + +# Make sure it exists +touch /cache/recovery/log + + +if [ "$AUTOAPPLY" == 1 -a "$WEBGET" == 0 -a "$GETUPDATE" == 0 ]; then + ECHO=echo + $ECHO "The --autoapplyupdate option is valid only in conjunction with --webget or --getupdate." + $ECHO "Aborting." + exit 1 +fi + +if [ "$COMPRESS" == 1 ]; then + $ECHO "Compressing with $DEFAULTCOMPRESSOR, level $DEFAULTLEVEL" +fi + +if [ "$WEBGET" == 1 ]; then + $ECHO "Fetching $WEBGETSOURCE to target folder: $WEBGETTARGET" +fi + +if [ ! "$SUBNAME" == "" ]; then + if [ "$BACKUP" == 1 ]; then + if [ "$COMPRESS" == 1 ]; then + $ECHO "Using $SUBNAME- prefix to create a compressed backup folder" + else + $ECHO "Using $SUBNAME- prefix to create a backup folder" + fi + else + if [ "$RESTORE" == 1 -o "$DELETE" == 1 -o "$COMPRESS" == 1 ]; then + $ECHO "Searching for backup directories, matching $SUBNAME, to delete or restore" + $ECHO "or compress" + $ECHO "" + fi + fi +else + if [ "$BACKUP" == 1 ]; then + $ECHO "Using G1 keyboard, enter a prefix substring and then " + $ECHO -n "or just to accept default: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBNAME + else + $ECHO "Accepting default." + fi + $ECHO "" + if [ "$COMPRESS" == 1 ]; then + $ECHO "Using $SUBNAME- prefix to create a compressed backup folder" + else + $ECHO "Using $SUBNAME- prefix to create a backup folder" + fi + $ECHO "" + else + if [ "$RESTORE" == 1 -o "$DELETE" == 1 -o "$COMPRESS" == 1 ]; then + $ECHO "Using G1 keyboard, enter a directory name substring and then " + $ECHO -n "to find matches or just to accept default: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBNAME + else + $ECHO "Accepting default." + fi + $ECHO "" + $ECHO "Using $SUBNAME string to search for matching backup directories" + $ECHO "" + fi + fi +fi + +if [ "$BACKUP" == 1 ]; then + mkyaffs2image=`which mkyaffs2image` + if [ "$mkyaffs2image" == "" ]; then + mkyaffs2image=`which mkyaffs2image-arm-uclibc` + if [ "$mkyaffs2image" == "" ]; then + $ECHO "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" + exit 1 + fi + fi + dump_image=`which dump_image` + if [ "$dump_image" == "" ]; then + dump_image=`which dump_image-arm-uclibc` + if [ "$dump_image" == "" ]; then + $ECHO "error: dump_image or dump_image-arm-uclibc not found in path" + exit 1 + fi + fi +fi + +if [ "$RESTORE" == 1 ]; then + flash_image=`which flash_image` + if [ "$flash_image" == "" ]; then + flash_image=`which flash_image-arm-uclibc` + if [ "$flash_image" == "" ]; then + $ECHO "error: flash_image or flash_image-arm-uclibc not found in path" + exit 1 + fi + fi + unyaffs=`which unyaffs` + if [ "$unyaffs" == "" ]; then + unyaffs=`which unyaffs-arm-uclibc` + if [ "$unyaffs" == "" ]; then + $ECHO "error: unyaffs or unyaffs-arm-uclibc not found in path" + exit 1 + fi + fi +fi +if [ "$COMPRESS" == 1 ]; then + compressor=`busybox | grep $DEFAULTCOMPRESSOR` + if [ "$compressor" == "" ]; then + $ECHO "Warning: busybox/$DEFAULTCOMPRESSOR is not found" + $ECHO "No compression operations will be performed" + COMPRESS=0 + else + $ECHO "Found $DEFAULTCOMPRESSOR, will compress the backup" + fi +fi + +# 1 +DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` +RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` +if [ "$RECOVERY" == "foo" ]; then + $ECHO "Error: Must be in recovery mode, aborting" + exit 1 +fi +if [ "$DEVICEID" == "foo" ]; then + $ECHO "Error: device id not found in /proc/cmdline, aborting" + exit 1 +fi +if [ ! "`id -u 2>/dev/null`" == "0" ]; then + if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then + $ECHO "Error: must run as root, aborting" + exit 1 + fi +fi + + +if [ "$RESTORE" == 1 ]; then + batteryAtLeast 30 +# ENERGY=`cat /sys/class/power_supply/battery/capacity` +# if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then +# ENERGY=100 +# fi +# if [ ! $ENERGY -ge 30 ]; then +# $ECHO "Error: not enough battery power" +# $ECHO "Connect charger or USB power and try again" +# exit 1 +# fi + + + umount /sdcard 2>/dev/null + mount /sdcard 2>/dev/null + if [ "`mount | grep sdcard`" == "" ]; then + $ECHO "error: unable to mount /sdcard, aborting" + exit 1 + fi + + # find the latest backup, but show the user other options + $ECHO "" + $ECHO "Looking for the latest backup, will display other choices!" + $ECHO "" + + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + $ECHO " " + + if [ "$RESTOREPATH" = "" ]; + then + $ECHO "Error: no backups found" + exit 2 + else + $ECHO "Default backup is the latest: $RESTOREPATH" + $ECHO "" + $ECHO "Other available backups are: " + $ECHO "" + ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT + $ECHO "" + $ECHO "Using G1 keyboard, enter a unique name substring to change it and " + $ECHO -n "or just to accept: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBSTRING + else + $ECHO "Accepting default." + SUBSTRING="" + fi + $ECHO "" + + if [ ! "$SUBSTRING" == "" ]; then + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep $SUBSTRING | tail -1` + else + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + fi + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no matching backups found, aborting" + exit 2 + fi + fi + + $ECHO "Restore path: $RESTOREPATH" + $ECHO "" + + umount /system /data 2>/dev/null + mount /system 2>/dev/null + mount /data 2>/dev/null + if [ "`mount | grep data`" == "" ]; then + $ECHO "error: unable to mount /data, aborting" + exit 1 + fi + if [ "`mount | grep system`" == "" ]; then + $ECHO "error: unable to mount /system, aborting" + exit 1 + fi + + CWD=$PWD + cd $RESTOREPATH + + DEFAULTEXT="" + if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 ]; then + DEFAULTCOMPRESSOR=bzip2 + DEFAULTDECOMPRESSOR=bunzip2 + DEFAULTEXT=.bz2 + fi + if [ `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then + DEFAULTCOMPRESSOR=gzip + DEFAULTDECOMPRESSOR=gunzip + DEFAULTEXT=.gz + fi + + if [ ! -f $RESTOREPATH/nandroid.md5$DEFAULTEXT ]; then + $ECHO "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" + exit 1 + fi + + if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 -o `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then + $ECHO "This backup is compressed with $DEFAULTCOMPRESSOR." + + # Make sure that $DEFAULT[DE]COMPRESSOR exists + if [ `busybox | grep $DEFAULTCOMPRESSOR | wc -l` -le 0 -a\ + `busybox | grep $DEFAULTDECOMPRESSOR | wc -l` -le 0 ]; then + + $ECHO "You do not have either the $DEFAULTCOMPRESSOR or the $DEFAULTDECOMPRESSOR" + $ECHO "to unpack this backup, cleaning up and aborting!" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi + $ECHO "Checking free space /sdcard for the decompression operation." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 100MB for gzip to uncompress the files + if [ $FREEBLOCKS -le 100000 ]; then + $ECHO "Error: not enough free space available on sdcard (need about 100mb)" + $ECHO "to perform restore from the compressed images, aborting." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi + $ECHO "Decompressing images, please wait...." + $ECHO "" + # Starting from the largest while we still have more space to reduce + # space requirements + $DEFAULTCOMPRESSOR -d `ls -S *$DEFAULTEXT` + $ECHO "Backup images decompressed" + $ECHO "" + fi + + $ECHO "Verifying backup images..." + md5sum -c nandroid.md5 + if [ $? -eq 1 ]; then + $ECHO "Error: md5sum mismatch, aborting" + exit 1 + fi + + if [ `ls boot* 2>/dev/null | wc -l` == 0 ]; then + NOBOOT=1 + fi + if [ `ls recovery* 2>/dev/null | wc -l` == 0 ]; then + NORECOVERY=1 + fi + if [ `ls data* 2>/dev/null | wc -l` == 0 ]; then + NODATA=1 + fi + if [ `ls system* 2>/dev/null | wc -l` == 0 ]; then + NOSYSTEM=1 + fi + # Amon_RA : If there's no ext backup set EXT2 to 0 so ext2 restore doesn't start + if [ `ls ext2* 2>/dev/null | wc -l` == 0 ]; then + EXT2=0 + fi + + for image in boot recovery; do + if [ "$NOBOOT" == "1" -a "$image" == "boot" ]; then + $ECHO "" + $ECHO "Not flashing boot image!" + $ECHO "" + continue + fi + if [ "$NORECOVERY" == "1" -a "$image" == "recovery" ]; then + $ECHO "" + $ECHO "Not flashing recovery image!" + $ECHO "" + continue + fi + $ECHO "Flashing $image..." + $flash_image $image $image.img $OUTPUT + done + + for image in data system; do + if [ "$NODATA" == "1" -a "$image" == "data" ]; then + $ECHO "" + $ECHO "Not restoring data image!" + $ECHO "" + continue + fi + if [ "$NOSYSTEM" == "1" -a "$image" == "system" ]; then + $ECHO "" + $ECHO "Not restoring system image!" + $ECHO "" + continue + fi + $ECHO "Erasing /$image..." + cd /$image + rm -rf * 2>/dev/null + $ECHO "Unpacking $image image..." + $unyaffs $RESTOREPATH/$image.img $OUTPUT + cd / + sync + umount /$image + done + + if [ "$EXT2" == 1 ]; then + # Amon_RA : Check if there's an ext partition before starting to restore + if [ -e /dev/block/mmcblk0p2 ]; then + $ECHO "Restoring the ext2 contents." + CWD=`pwd` + cd / + + if [ `mount | grep /system | wc -l` == 0 ]; then + mount /system + else + mount -o rw,remount /system + fi + + if [ `mount | grep /system/sd | wc -l` == 0 ]; then + mount /system/sd + fi + + cd $CWD + CHECK=`mount | grep /system/sd` + + if [ "$CHECK" == "" ]; then + $ECHO "Warning: --ext2 specified but unable to mount the ext2 partition." + $ECHO "Warning: your phone may be in an inconsistent state on reboot." + exit 1 + else + CWD=`pwd` + cd /system/sd + # Depending on whether the ext2 backup is compressed we do either or. + if [ -e $RESTOREPATH/ext2.tar ]; then + rm -rf * 2>/dev/null + tar -x$TARFLAGS -f $RESTOREPATH/ext2.tar + else + if [ -e $RESTOREPATH/ext2.tgz ]; then + rm -rf * 2>/dev/null + tar -x$TARFLAGS -zf $RESTOREPATH/ext2.tgz + else + if [ -e $RESTOREPATH/ext2.tar.bz2 ]; then + rm -rf * 2>/dev/null + tar -x$TARFLAGS -jf $RESTOREPATH/ext2.tar.bz2 + else + $ECHO "Warning: --ext2 specified but cannot find the ext2 backup." + $ECHO "Warning: your phone may be in an inconsistent state on reboot." + fi + fi + fi + cd $CWD + sync + umount /system/sd + umount /system + + fi + else + # Amon_RA : Just display a warning + $ECHO "Warning: --ext2 specified but ext2 partition present on sdcard" + $ECHO "Warning: your phone may be in an inconsistent state on reboot." + fi + fi + $ECHO "Restore done" + exit 0 +fi + +# 2. +if [ "$BACKUP" == 1 ]; then + + if [ "$COMPRESS" == 1 ]; then + ENERGY=`cat /sys/class/power_supply/battery/capacity` + if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then + ENERGY=100 + fi + if [ ! $ENERGY -ge 30 ]; then + $ECHO "Warning: Not enough battery power to perform compression." + COMPRESS=0 + $ECHO "Turning off compression option, you can compress the backup later" + $ECHO "with the compression options." + fi + fi + +$ECHO "mounting system and data read-only, sdcard read-write" +umount /system 2>/dev/null +umount /data 2>/dev/null +umount /sdcard 2>/dev/null +mount -o ro /system || FAIL=1 +mount -o ro /data || FAIL=2 +mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 +case $FAIL in + 1) $ECHO "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; + 2) $ECHO "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; + 3) $ECHO "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; +esac + +if [ ! "$SUBNAME" == "" ]; then + SUBNAME=$SUBNAME- +fi + +# Identify the backup with what partitions have been backed up +if [ "$NOBOOT" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"B" +fi +if [ "$NOCACHE" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"C" +fi +if [ "$NODATA" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"D" +fi +if [ "$EXT2" == 1 ]; then + BACKUPLEGEND=$BACKUPLEGEND"E" +fi +if [ "$NOMISC" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"M" +fi +if [ "$NORECOVERY" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"R" +fi +if [ "$NOSYSTEM" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"S" +fi + +if [ ! -e /dev/mtd/mtd6ro ]; then + NOSPLASH1=1 + NOSPLASH2=1 +fi + +if [ "$NOSPLASH1" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"1" +fi +if [ "$NOSPLASH2" == 0 ]; then + BACKUPLEGEND=$BACKUPLEGEND"2" +fi + +if [ ! "$BACKUPLEGEND" == "" ]; then + BACKUPLEGEND=$BACKUPLEGEND- +fi + + +TIMESTAMP="`date +%Y%m%d-%H%M`" +DESTDIR="$BACKUPPATH/$SUBNAME$BACKUPLEGEND$TIMESTAMP" +if [ ! -d $DESTDIR ]; then + mkdir -p $DESTDIR + if [ ! -d $DESTDIR ]; then + $ECHO "error: cannot create $DESTDIR" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi +else + touch $DESTDIR/.nandroidwritable + if [ ! -e $DESTDIR/.nandroidwritable ]; then + $ECHO "error: cannot write to $DESTDIR" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 + fi + rm $DESTDIR/.nandroidwritable +fi + +# 3. +$ECHO "checking free space on sdcard" +FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" +# we need about 130MB for the dump +if [ $FREEBLOCKS -le 130000 ]; then + $ECHO "Error: not enough free space available on sdcard (need 130mb), aborting." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + exit 1 +fi + + + +if [ -e /dev/mtd/mtd6ro ]; then + if [ "$NOSPLASH1" == 0 ]; then + $ECHO -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." + dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash1.img skip=19072 bs=2048 count=150 2>/dev/null + $ECHO "done" + sleep 1s + else + $ECHO "Dump of the splash1 image suppressed." + fi + if [ "$NOSPLASH2" == 0 ]; then + $ECHO -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." + dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash2.img skip=19456 bs=2048 count=150 2>/dev/null + $ECHO "done" + else + $ECHO "Dump of the splash2 image suppressed." + fi +fi + + +# 5. +for image in boot recovery misc; do + + case $image in + boot) + if [ "$NOBOOT" == 1 ]; then + $ECHO "Dump of the boot partition suppressed." + continue + fi + ;; + recovery) + if [ "$NORECOVERY" == 1 ]; then + $ECHO "Dump of the recovery partition suppressed." + continue + fi + ;; + misc) + if [ "$NOMISC" == 1 ]; then + $ECHO "Dump of the misc partition suppressed." + continue + fi + ;; + esac + + # 5a + DEVICEMD5=`$dump_image $image - | md5sum | awk '{ print $1 }'` + sleep 1s + MD5RESULT=1 + # 5b + $ECHO -n "Dumping $image to $DESTDIR/$image.img..." + ATTEMPT=0 + while [ $MD5RESULT -eq 1 ]; do + let ATTEMPT=$ATTEMPT+1 + # 5b1 + $dump_image $image $DESTDIR/$image.img $OUTPUT + sync + # 5b3 + echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - $OUTPUT + if [ $? -eq 1 ]; then + true + else + MD5RESULT=0 + fi + if [ "$ATTEMPT" == "5" ]; then + $ECHO "Fatal error while trying to dump $image, aborting." + umount /system + umount /data + umount /sdcard + exit 1 + fi + done + $ECHO "done" +done + +# 6 +for image in system data cache; do + case $image in + system) + if [ "$NOSYSTEM" == 1 ]; then + $ECHO "Dump of the system partition suppressed." + continue + fi + ;; + data) + if [ "$NODATA" == 1 ]; then + $ECHO "Dump of the data partition suppressed." + continue + fi + ;; + cache) + if [ "$NOCACHE" == 1 ]; then + $ECHO "Dump of the cache partition suppressed." + continue + fi + ;; + esac + + # 6a + $ECHO -n "Dumping $image to $DESTDIR/$image.img..." + $mkyaffs2image /$image $DESTDIR/$image.img $OUTPUT + sync + $ECHO "done" +done + +# Backing up the ext2 partition, not really for the backup but to switch ROMS and apps at the same time. + +if [ "$EXT2" == 1 ]; then + $ECHO "Storing the ext2(Apps, Dalvik-cache) contents in the backup folder." + + CHECK1=`mount | grep /system` + if [ "$CHECK1" == "" ]; then + mount /system 2>/dev/null + fi + CHECK2=`mount | grep /system/sd` + if [ "$CHECK2" == "" ]; then + mount /system/sd 2>/dev/null + fi + + CHECK1=`mount | grep /system` + CHECK2=`mount | grep /system/sd` + if [ "$CHECK1" == "" -o "$CHECK2" == "" ]; then + $ECHO "Warning: --ext2 specified but unable to mount the ext2 partition." + exit 1 + else + + CWD=`pwd` + cd /system/sd + # Depending on the whether we want it compressed we do either or. + if [ "$COMPRESS" == 0 ]; then + tar -cvf $DESTDIR/ext2.tar ./ + else + if [ "$DEFAULTCOMPRESSOR" == "bzip2" ]; then + tar -cvjf $DESTDIR/ext2.tar.bz2 ./ + else + tar -cvzf $DESTDIR/ext2.tgz ./ + fi + fi + cd $CWD + umount /system/sd + fi +fi + + +# 7. +$ECHO -n "generating md5sum file..." +CWD=$PWD +cd $DESTDIR +md5sum *img > nandroid.md5 + +# 7b. +if [ "$COMPRESS" == 1 ]; then + $ECHO "Compressing the backup, may take a bit of time, please wait..." + $ECHO "checking free space on sdcard for the compression operation." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 70MB for the intermediate storage needs + if [ $FREEBLOCKS -le 70000 ]; then + $ECHO "error: not enough free space available on sdcard for compression operation (need 70mb)" + $ECHO "leaving this backup uncompressed." + else + # we are already in $DESTDIR, start compression from the smallest files + # to maximize space for the largest's compression, less likely to fail. + # To decompress reverse the order. + $DEFAULTCOMPRESSOR $DEFAULTLEVEL `ls -S -r * | grep -v ext2` + fi +fi + +cd $CWD +$ECHO "done" + +# 8. +$ECHO "unmounting system, data and sdcard" +umount /system +umount /data +umount /sdcard + +# 9. +$ECHO "Backup successful." +if [ "$AUTOREBOOT" == 1 ]; then + reboot +fi +exit 0 +fi + + +# ----------------------------------GETTING UPDATES DIRECT FROM THE WEB USING WIFI------------- + +if [ "$WEBGET" == 1 ]; then + $ECHO "mounting system and data read-only, sdcard read-write" + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + + # Since we are in recovery, these file-systems have to be mounted + $ECHO "Mounting /system and /data for starting WiFi" + mount -o ro /system || FAIL=1 + # Need to write to this system to setup nameservers for the wifi + mount -o rw /data || FAIL=2 + mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 + + case $FAIL in + 1) $ECHO "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; + 2) $ECHO "Error mounting data read-write"; umount /system /data /sdcard; exit 1;; + 3) $ECHO "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; + esac + + if [ "$WEBGETSOURCE" == "" ]; then + # Set the URL to the current latest update + if [ "$ITSANUPDATE" == 1 ]; then + WEBGETSOURCE=$DEFAULTWEBUPDATE + else + WEBGETSOURCE=$DEFAULTWEBIMAGE + fi + fi + + if [ "$AUTOAPPLY" == 0 ]; then + # Need to check space on sdcard only if we dump the update there. + $ECHO "Checking free space on sdcard for the update download." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 50MB for the storage needs + if [ $FREEBLOCKS -le 50000 ]; then + $ECHO "Error: not enough free space available on sdcard for the update operation (need 50mb)" + $ECHO "Please free up space before invoking this option again." + $ECHO "Cleaning up, unmounting file systems, aborting." + umount /system /data /sdcard + exit 1 + fi + fi + + if [ ! `basename $WEBGETSOURCE` == `basename $WEBGETSOURCE .zip` ]; then + # It is a zip, not img. + ITSANUPDATE=1 + else + if [ ! `basename $WEBGETSOURCE` == `basename $WEBGETSOURCE .img` ]; then + # It is an img file. + ITSANIMAGE=1 + else + # Unknown file type + $ECHO "Unknown file type, cleaning up, aborting." + umount /system /data /sdcard + exit 1 + fi + fi + + + if [ "$ITSANUPDATE" == 1 -a "$AUTOAPPLY" == 0 ]; then + # Move the previous update aside, if things go badly with the new update, it is good + # to have the last one still around :-) + + # If we cannot figure out what the file name used to be, create this new one with a time stamp + OLDNAME="OLD-update-`date +%Y%m%d-%H%M`" + + if [ -e $WEBGETTARGET/update.zip ]; then + $ECHO "There is already an update.zip in $WEBGETTARGET, backing it up to" + if [ -e $WEBGETTARGET/update.name ]; then + OLDNAME=`cat $WEBGETTARGET/update.name` + # Backup the name file (presumably contains the old name of the update.zip + mv -f $WEBGETTARGET/update.name $WEBGETTARGET/`basename $OLDNAME .zip`.name + fi + $ECHO "`basename $OLDNAME .zip`.zip" + mv -f $WEBGETTARGET/update.zip $WEBGETTARGET/`basename $OLDNAME .zip`.zip + + # Backup the MD5sum file + if [ -e $WEBGETTARGET/update.MD5sum ]; then + mv -f $WEBGETTARGET/update.MD5sum $WEBGETTARGET/`basename $OLDNAME .zip`.MD5sum + fi + fi + fi + + $ECHO "Starting WiFI, please wait..." + insmod /system/lib/modules/wlan.ko + + wlan_loader -f /system/etc/wifi/Fw1251r1c.bin -e /proc/calibration -i /system/etc/wifi/tiwlan.ini + + CWD=`pwd` + cd /data/local/tmp + + wpa_supplicant -f -Dtiwlan0 -itiwlan0 -c/data/misc/wifi/wpa_supplicant.conf& + + sleep 5 + $ECHO "wpa_supplicant started" + $ECHO "" + + echo "nameserver $NAMESERVER1" >/etc/resolv.conf + echo "nameserver $NAMESERVER2" >>/etc/resolv.conf + + #We want the wifi to assign a dynamic address + $ECHO "Starting DHCPCD server (dynamic address assignment)" + # -BKL flags???? + dhcpcd -d tiwlan0 2>/dev/null & + + # Have to wait for it to init stuff + sleep 10 + + + CHECK1=`ps | grep -v grep | grep dhcpcd` + CHECK2=`ps | grep -v grep | grep wpa_supplicant` + if [ "$CHECK1" == "" -o "$CHECK2" == "" ]; then + $ECHO "Error: wpa_supplicant or DHCPCD server is not running, cleaning up, aborting" + rm -- -Dtiwlan0 + cd $CWD + + $ECHO "unmounting /system, /data and /sdcard" + umount /system + umount /data + umount /sdcard + exit 2 + fi + + $ECHO "DHCPCD server started" + $ECHO "" + + $ECHO "WiFi is running!" + $ECHO "" + + if [ "$AUTOAPPLY" == 1 ]; then + $ECHO "Autoapply is on, retrieving the update into /cache/`basename $WEBGETSOURCE`" + + wget -O /cache/`basename $WEBGETSOURCE` $WEBGETSOURCE $OUTPUT + + if [ ! -e /cache/recovery ]; then + mkdir /cache/recovery + chmod 777 /cache/recovery + fi + if [ -e /cache/recovery/command ]; then + echo "--update_package=CACHE:`basename $WEBGETSOURCE`" >>/cache/recovery/command + else + echo "--update_package=CACHE:`basename $WEBGETSOURCE`" >/cache/recovery/command + fi + chmod 555 /cache/recovery/command + # Once rebooted the update will be applied. + + else + + if [ "$ITSANUPDATE" == 1 ]; then + $ECHO "Retrieving system update into $WEBGETTARGET/update.zip, please wait..." + wget -O $WEBGETTARGET/update.zip $WEBGETSOURCE $OUTPUT + + echo "`basename $WEBGETSOURCE`" > $WEBGETTARGET/update.name + $ECHO "" + $ECHO "Update retrieved, if concerned, please compare the md5sum with the number" + $ECHO "you see on the web page, if it is NOT the same, the retrieval" + $ECHO "has failed and has to be repeated." + $ECHO "" + $ECHO `md5sum $WEBGETTARGET/update.zip | tee $WEBGETTARGET/update.MD5sum` + $ECHO "" + $ECHO "MD5sum has been stored in $WEBGETTARGET/update.MD5sum" + else + $ECHO "Retrieving the image into $WEBGETTARGET/`basename $WEBGETSOURCE`, please wait..." + wget -O $WEBGETTARGET/`basename $WEBGETSOURCE` $WEBGETSOURCE $OUTPUT + $ECHO "" + $ECHO "$WEBGETSOURCE retrieved, if concerned, please compare the md5sum with the number" + $ECHO "you see on the web page, if it is NOT the same, the retrieval" + $ECHO "has failed and has to be repeated." + $ECHO "" + md5sum $WEBGETTARGET/`basename $WEBGETSOURCE` | tee $WEBGETTARGET/`basename $WEBGETSOURCE .img`.MD5sum $OUTPUT + $ECHO "" + $ECHO "MD5sum has been stored in $WEBGETTARGET/`basename $WEBGETSOURCE .img`.MD5sum" + $ECHO "" + $ECHO -n "Would you like to flash this image into boot or recovery? (or no for no flash) " + read ANSWER + if [ "$ANSWER" == "boot" ]; then + $ECHO "Flashing $WEBGETTARGET/`basename $WEBGETSOURCE` into the boot partition." + $flash_image boot $WEBGETTARGET/`basename $WEBGETSOURCE` + else + if [ "$ANSWER" == "recovery" ]; then + $ECHO "Moving $WEBGETTARGET/`basename $WEBGETSOURCE` into the /data/recovery.img" + $ECHO "and /system/recovery.img" + cp -f $WEBGETTARGET/`basename $WEBGETSOURCE` /data/recovery.img + mount -o rw,remount /system + cp -f $WEBGETTARGET/`basename $WEBGETSOURCE` /system/recovery.img + $ECHO "Depending on the settings of your specific ROM, the recovery.img will be" + $ECHO "flashed at the normal bootup time either from /system or /data." + else + $ECHO "Not flashing the image." + fi + fi + fi + $ECHO "" + + fi + + $ECHO "Shutting down DHCPCD service and wpa_supplicant" + killall -TERM dhcpcd + TEMPVAR=`ps | grep -v grep | grep wpa_supplicant` + TEMPVAR=`echo $TEMPVAR | cut -f 1 -d ' '` + kill -TERM $TEMPVAR + + while true; do + CHECK=`ps | grep -v grep | grep dhcpcd` + if [ ! "$CHECK" == "" ]; then + sleep 1 + else + break + fi + done + + while true; do + CHECK=`ps | grep -v grep | grep wpa_supplicant` + if [ ! "$CHECK" == "" ]; then + sleep 1 + else + break + fi + done + #sleep 5 + + $ECHO "Cleaning up..." + # Looks like cannot clean up wlan module since chdir is missing + #rmmod wlan + rm -- -Dtiwlan0 + cd $CWD + + $ECHO "unmounting /system, /data and /sdcard" + umount /system + umount /data + umount /sdcard + + if [ "$AUTOAPPLY" == 1 ]; then + $ECHO "Auto apply update is on, rebooting into recovery to apply the update." + $ECHO "When the update is complete reboot into the normal mode." + $ECHO "The device will reboot and the update will be applied in 10 seconds!" + sleep 10 + reboot recovery + else + if [ "$ITSANUPDATE" == 1 ]; then + $ECHO "If you put the update into a folder other than /sdcard you need to use --getupdate to" + $ECHO "prepare the update for application." + $ECHO "You may want to execute 'reboot recovery' and choose update option to flash the update." + $ECHO "Or in the alternative, shutdown your phone with reboot -p, and then press +" + $ECHO "to initiate a normal system update procedure, if you have stock SPL." + fi + exit 0 + fi +fi + +# -------------------------------------DELETION, COMPRESSION OF BACKUPS--------------------------------- +if [ "$COMPRESS" == 1 -o "$DELETE" == 1 ]; then + $ECHO "Unmounting /system and /data to be on the safe side, mounting /sdcard read-write." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + + FAIL=0 + # Since we are in recovery, these file-system have to be mounted + $ECHO "Mounting /sdcard to look for backups." + mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=1 + + if [ "$FAIL" == 1 ]; then + $ECHO "Error mounting /sdcard read-write, cleaning up..."; umount /system /data /sdcard; exit 1 + fi + + $ECHO "The current size of /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + $ECHO "" + + # find the oldest backup, but show the user other options + $ECHO "Looking for the oldest backup to delete, newest to compress," + $ECHO "will display all choices!" + $ECHO "" + $ECHO "Here are the backups you have picked within this repository $BACKUPPATH:" + + if [ "$DELETE" == 1 ]; then + RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null $OUTPUT + else + RESTOREPATH=`ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null $OUTPUT + fi + $ECHO " " + + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no backups found" + exit 2 + else + if [ "$DELETE" == 1 ]; then + $ECHO "Default backup to delete is the oldest: $RESTOREPATH" + $ECHO "" + $ECHO "Other candidates for deletion are: " + ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT + fi + if [ "$COMPRESS" == 1 ]; then + $ECHO "Default backup to compress is the latest: $RESTOREPATH" + $ECHO "" + $ECHO "Other candidates for compression are: " + ls -trd $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep -v $RESTOREPATH $OUTPUT + fi + + $ECHO "" + $ECHO "Using G1 keyboard, enter a unique name substring to change it and " + $ECHO -n "or just to accept: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBSTRING + else + $ECHO "Accepting default." + SUBSTRING="" + fi + + if [ ! "$SUBSTRING" == "" ]; then + RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | grep $SUBSTRING | tail -1` + else + RESTOREPATH=`ls -td $BACKUPPATH/*$SUBNAME* 2>/dev/null | tail -1` + fi + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no matching backup found, aborting" + exit 2 + fi + fi + + if [ "$DELETE" == 1 ]; then + $ECHO "Deletion path: $RESTOREPATH" + $ECHO "" + $ECHO "WARNING: Deletion of a backup is an IRREVERSIBLE action!!!" + $ECHO -n "Are you absolutely sure? {yes | YES | Yes | no | NO | No}: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read ANSWER + else + ANSWER=yes + $ECHO "Accepting default." + fi + $ECHO "" + if [ "$ANSWER" == "yes" -o "$ANSWER" == "YES" -o "$ANSWER" == "Yes" ]; then + rm -rf $RESTOREPATH + $ECHO "" + $ECHO "$RESTOREPATH has been permanently removed from your SDCARD." + $ECHO "Post deletion size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + else + if [ "$ANSWER" == "no" -o "$ANSWER" == "NO" -o "$ANSWER" == "No" ]; then + $ECHO "The chosen backup will NOT be removed." + else + $ECHO "Invalid answer: assuming NO." + fi + fi + fi + + if [ "$COMPRESS" == 1 ]; then + + CWD=`pwd` + cd $RESTOREPATH + + if [ `ls *.bz2 2>/dev/null|wc -l` -ge 1 -o `ls *.gz 2>/dev/null|wc -l` -ge 1 ]; then + $ECHO "This backup is already compressed, cleaning up, aborting..." + cd $CWD + umount /sdcard 2>/dev/null + exit 0 + fi + + $ECHO "checking free space on sdcard for the compression operation." + FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" + # we need about 70MB for the intermediate storage needs + if [ $FREEBLOCKS -le 70000 ]; then + $ECHO "Error: not enough free space available on sdcard for compression operation (need 70mb)" + $ECHO "leaving this backup uncompressed." + else + # we are already in $DESTDIR, start compression from the smallest files + # to maximize space for the largest's compression, less likely to fail. + # To decompress reverse the order. + $ECHO "Pre compression size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + $ECHO "" + $ECHO "Compressing the backup may take a bit of time, please wait..." + $DEFAULTCOMPRESSOR $DEFAULTLEVEL `ls -S -r *` + $ECHO "" + $ECHO "Post compression size of the /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + fi + fi + + $ECHO "Cleaning up." + cd $CWD + umount /sdcard 2>/dev/null + exit 0 + +fi + +if [ "$GETUPDATE" == 1 ]; then + $ECHO "Unmounting /system and /data to be on the safe side, mounting /sdcard read-write." + umount /system 2>/dev/null + umount /data 2>/dev/null + umount /sdcard 2>/dev/null + + FAIL=0 + # Since we are in recovery, these file-system have to be mounted + $ECHO "Mounting /sdcard to look for updates to flash." + mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=1 + + if [ "$FAIL" == 1 ]; then + $ECHO "Error mounting /sdcard read-write, cleaning up..."; umount /system /data /sdcard; exit 1 + fi + + $ECHO "The current size of /sdcard FAT32 filesystem is `du /sdcard | tail -1 | cut -f 1 -d '/'`Kb" + $ECHO "" + + # find all the files with update in them, but show the user other options + $ECHO "Looking for all *update*.zip candidate files to flash." + $ECHO "" + $ECHO "Here are the updates limited by the subname $SUBNAME found" + $ECHO "within the repository $DEFAULTUPDATEPATH:" + $ECHO "" + RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | tail -1` + if [ "$RESTOREPATH" == "" ]; then + $ECHO "Error: found no matching updates, cleaning up, aborting..." + umount /sdcard 2>/dev/null + exit 2 + fi + ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update $OUTPUT + $ECHO "" + $ECHO "The default update is the latest $RESTOREPATH" + $ECHO "" + $ECHO "Using G1 keyboard, enter a unique name substring to change it and " + $ECHO -n "or just to accept: " + if [ "$ASSUMEDEFAULTUSERINPUT" == 0 ]; then + read SUBSTRING + else + $ECHO "Accepting default." + SUBSTRING="" + fi + $ECHO "" + + if [ ! "$SUBSTRING" == "" ]; then + RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | grep $SUBSTRING | tail -1` + else + RESTOREPATH=`ls -trd $DEFAULTUPDATEPATH/*$SUBNAME*.zip 2>/dev/null | grep update | tail -1` + fi + if [ "$RESTOREPATH" = "" ]; then + $ECHO "Error: no matching backups found, aborting" + exit 2 + fi + + if [ "$RESTOREPATH" == "/sdcard/update.zip" ]; then + $ECHO "You chose update.zip, it is ready for flashing, there nothing to do." + else + + # Things seem ok so far. + + # Move the previous update aside, if things go badly with the new update, it is good + # have the last one still around :-) + + # If we cannot figure out what the file name used to be, create this new one with a time stamp + OLDNAME="OLD-update-`date +%Y%m%d-%H%M`" + + if [ -e /sdcard/update.zip ]; then + $ECHO "There is already an update.zip in /sdcard, backing it up to" + if [ -e /sdcard/update.name ]; then + OLDNAME=`cat /sdcard/update.name` + # Backup the name file (presumably contains the old name of the update.zip + mv -f /sdcard/update.name /sdcard/`basename $OLDNAME .zip`.name + fi + $ECHO "`basename $OLDNAME .zip`.zip" + mv -f /sdcard/update.zip /sdcard/`basename $OLDNAME .zip`.zip + + # Backup the MD5sum file + if [ -e /sdcard/update.MD5sum ]; then + mv -f /sdcard/update.MD5sum /sdcard/`basename $OLDNAME .zip`.MD5sum + fi + fi + + if [ -e $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.MD5sum ]; then + mv -f $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.MD5sum /sdcard/update.MD5sum + else + $ECHO `md5sum $RESTOREPATH | tee /sdcard/update.MD5sum` + $ECHO "" + $ECHO "MD5sum has been stored in /sdcard/update.MD5sum" + $ECHO "" + fi + if [ -e $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.name ]; then + mv -f $DEFAULTUPDATEPATH/`basename $RESTOREPATH .zip`.name /sdcard/update.name + else + echo "`basename $RESTOREPATH`" > /sdcard/update.name + fi + + mv -i $RESTOREPATH /sdcard/update.zip + + + $ECHO "Your file $RESTOREPATH has been moved to the root of sdcard, and is ready for flashing!!!" + + fi + + $ECHO "You may want to execute 'reboot recovery' and then choose the update option to flash the update." + $ECHO "Or in the alternative, shutdown your phone with reboot -p, and then press + to" + $ECHO "initiate a standard update procedure if you have stock SPL." + $ECHO "" + $ECHO "Cleaning up and exiting." + umount /sdcard 2>/dev/null + exit 0 +fi diff --git a/nandroid/unyaffs.c b/nandroid/unyaffs.c new file mode 100644 index 0000000..aa8a735 --- /dev/null +++ b/nandroid/unyaffs.c @@ -0,0 +1,118 @@ +/* + * unyaffs: extract files from yaffs2 file system image to current directory + * + * Created by Kai Wei + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "unyaffs.h" + +#define CHUNK_SIZE 2048 +#define SPARE_SIZE 64 +#define MAX_OBJECTS 10000 +#define YAFFS_OBJECTID_ROOT 1 + + +unsigned char data[CHUNK_SIZE + SPARE_SIZE]; +unsigned char *chunk_data = data; +unsigned char *spare_data = data + CHUNK_SIZE; +int img_file; + +char *obj_list[MAX_OBJECTS]; +void process_chunk() +{ + int out_file, remain, s; + char *full_path_name; + + yaffs_PackedTags2 *pt = (yaffs_PackedTags2 *)spare_data; + if (pt->t.byteCount == 0xffff) { //a new object + + yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)malloc(sizeof(yaffs_ObjectHeader)); + memcpy(oh, chunk_data, sizeof(yaffs_ObjectHeader)); + + full_path_name = (char *)malloc(strlen(oh->name) + strlen(obj_list[oh->parentObjectId]) + 2); + if (full_path_name == NULL) { + perror("malloc full path name\n"); + } + strcpy(full_path_name, obj_list[oh->parentObjectId]); + strcat(full_path_name, "/"); + strcat(full_path_name, oh->name); + obj_list[pt->t.objectId] = full_path_name; + + switch(oh->type) { + case YAFFS_OBJECT_TYPE_FILE: + remain = oh->fileSize; + out_file = creat(full_path_name, oh->yst_mode); + while(remain > 0) { + if (read_chunk()) + return -1; + s = (remain < pt->t.byteCount) ? remain : pt->t.byteCount; + if (write(out_file, chunk_data, s) == -1) + return -1; + remain -= s; + } + close(out_file); + break; + case YAFFS_OBJECT_TYPE_SYMLINK: + symlink(oh->alias, full_path_name); + break; + case YAFFS_OBJECT_TYPE_DIRECTORY: + mkdir(full_path_name, 0777); + break; + case YAFFS_OBJECT_TYPE_HARDLINK: + link(obj_list[oh->equivalentObjectId], full_path_name); + break; + } + } +} + + +int read_chunk() +{ + ssize_t s; + int ret = -1; + memset(chunk_data, 0xff, sizeof(chunk_data)); + s = read(img_file, data, CHUNK_SIZE + SPARE_SIZE); + if (s == -1) { + perror("read image file\n"); + } else if (s == 0) { + printf("end of image\n"); + } else if ((s == (CHUNK_SIZE + SPARE_SIZE))) { + ret = 0; + } else { + fprintf(stderr, "broken image file\n"); + } + return ret; +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + printf("Usage: unyaffs image_file_name\n"); + exit(1); + } + img_file = open(argv[1], O_RDONLY); + if (img_file == -1) { + printf("open image file failed\n"); + exit(1); + } + + obj_list[YAFFS_OBJECTID_ROOT] = "."; + while(1) { + if (read_chunk() == -1) + break; + process_chunk(); + } + close(img_file); + return 0; +} diff --git a/nandroid/unyaffs.h b/nandroid/unyaffs.h new file mode 100644 index 0000000..fcd8046 --- /dev/null +++ b/nandroid/unyaffs.h @@ -0,0 +1,144 @@ +/* + * definition copied from yaffs project + */ + +#ifndef __UNYAFFS_H__ +#define __UNYAFFS_H__ + + +#define YAFFS_MAX_NAME_LENGTH 255 +#define YAFFS_MAX_ALIAS_LENGTH 159 + +#include + +/* Definition of types */ +#ifndef __ASM_ARM_TYPES_H +typedef unsigned char __u8; +typedef unsigned short __u16; +typedef unsigned __u32; +#endif +typedef struct { + unsigned sequenceNumber; + unsigned objectId; + unsigned chunkId; + unsigned byteCount; +} yaffs_PackedTags2TagsPart; + +typedef struct { + unsigned char colParity; + unsigned lineParity; + unsigned lineParityPrime; +} yaffs_ECCOther; + +typedef struct { + yaffs_PackedTags2TagsPart t; + yaffs_ECCOther ecc; +} yaffs_PackedTags2; + +typedef enum { + YAFFS_ECC_RESULT_UNKNOWN, + YAFFS_ECC_RESULT_NO_ERROR, + YAFFS_ECC_RESULT_FIXED, + YAFFS_ECC_RESULT_UNFIXED +} yaffs_ECCResult; + +typedef enum { + YAFFS_OBJECT_TYPE_UNKNOWN, + YAFFS_OBJECT_TYPE_FILE, + YAFFS_OBJECT_TYPE_SYMLINK, + YAFFS_OBJECT_TYPE_DIRECTORY, + YAFFS_OBJECT_TYPE_HARDLINK, + YAFFS_OBJECT_TYPE_SPECIAL +} yaffs_ObjectType; + + +typedef struct { + + unsigned validMarker0; + unsigned chunkUsed; /* Status of the chunk: used or unused */ + unsigned objectId; /* If 0 then this is not part of an object (unused) */ + unsigned chunkId; /* If 0 then this is a header, else a data chunk */ + unsigned byteCount; /* Only valid for data chunks */ + + /* The following stuff only has meaning when we read */ + yaffs_ECCResult eccResult; + unsigned blockBad; + + /* YAFFS 1 stuff */ + unsigned chunkDeleted; /* The chunk is marked deleted */ + unsigned serialNumber; /* Yaffs1 2-bit serial number */ + + /* YAFFS2 stuff */ + unsigned sequenceNumber; /* The sequence number of this block */ + + /* Extra info if this is an object header (YAFFS2 only) */ + + unsigned extraHeaderInfoAvailable; /* There is extra info available if this is not zero */ + unsigned extraParentObjectId; /* The parent object */ + unsigned extraIsShrinkHeader; /* Is it a shrink header? */ + unsigned extraShadows; /* Does this shadow another object? */ + + yaffs_ObjectType extraObjectType; /* What object type? */ + + unsigned extraFileLength; /* Length if it is a file */ + unsigned extraEquivalentObjectId; /* Equivalent object Id if it is a hard link */ + + unsigned validMarker1; + +} yaffs_ExtendedTags; + +/* -------------------------- Object structure -------------------------------*/ +/* This is the object structure as stored on NAND */ + +typedef struct { + yaffs_ObjectType type; + + /* Apply to everything */ + int parentObjectId; + __u16 sum__NoLongerUsed; /* checksum of name. No longer used */ + char name[YAFFS_MAX_NAME_LENGTH + 1]; + + /* The following apply to directories, files, symlinks - not hard links */ + __u32 yst_mode; /* protection */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 notForWinCE[5]; +#else + __u32 yst_uid; + __u32 yst_gid; + __u32 yst_atime; + __u32 yst_mtime; + __u32 yst_ctime; +#endif + + /* File size applies to files only */ + int fileSize; + + /* Equivalent object id applies to hard links only. */ + int equivalentObjectId; + + /* Alias is for symlinks only. */ + char alias[YAFFS_MAX_ALIAS_LENGTH + 1]; + + __u32 yst_rdev; /* device stuff for block and char devices (major/min) */ + +#ifdef CONFIG_YAFFS_WINCE + __u32 win_ctime[2]; + __u32 win_atime[2]; + __u32 win_mtime[2]; +#else + __u32 roomToGrow[6]; + +#endif + __u32 inbandShadowsObject; + __u32 inbandIsShrink; + + __u32 reservedSpace[2]; + int shadowsObject; /* This object header shadows the specified object if > 0 */ + + /* isShrink applies to object headers written when we shrink the file (ie resize) */ + __u32 isShrink; + +} yaffs_ObjectHeader; + +#endif diff --git a/res/nandroid-mobile.sh b/res/nandroid-mobile.sh deleted file mode 100755 index 9a9a824..0000000 --- a/res/nandroid-mobile.sh +++ /dev/null @@ -1,293 +0,0 @@ -#!/sbin/sh - -# nandroid v2.1 - an Android backup tool for the G1 by infernix and brainaid - -# Requirements: - -# - a modded android in recovery mode (JF 1.3 will work by default) -# - adb shell as root in recovery mode if not using a pre-made recovery image -# - busybox in recovery mode -# - dump_image-arm-uclibc compiled and in path on phone -# - mkyaffs2image-arm-uclibc compiled and installed in path on phone - -# Reference data: - -# dev: size erasesize name -#mtd0: 00040000 00020000 "misc" -#mtd1: 00500000 00020000 "recovery" -#mtd2: 00280000 00020000 "boot" -#mtd3: 04380000 00020000 "system" -#mtd4: 04380000 00020000 "cache" -#mtd5: 04ac0000 00020000 "userdata" -#mtd6 is everything, dump splash1 with: dd if=/dev/mtd/mtd6ro of=/sdcard/splash1.img skip=19072 bs=2048 count=150 - -# We don't dump misc or cache because they do not contain any useful data that we are aware of at this time. - -# Logical steps (v2.1): -# -# 0. test for a target dir and the various tools needed, if not found then exit with error. -# 1. check "adb devices" for a device in recovery mode. set DEVICEID variable to the device ID. abort when not found. -# 2. mount system and data partitions read-only, set up adb portforward and create destdir -# 3. check free space on /cache, exit if less blocks than 20MB free -# 4. push required tools to device in /cache -# 5 for partitions boot recovery misc: -# 5a get md5sum for content of current partition *on the device* (no data transfered) -# 5b while MD5sum comparison is incorrect (always is the first time): -# 5b1 dump current partition to a netcat session -# 5b2 start local netcat to dump image to current dir -# 5b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) -# 6 for partitions system data: -# 6a get md5sum for tar of content of current partition *on the device* (no data transfered) -# 6b while MD5sum comparison is incorrect (always is the first time): -# 6b1 tar current partition to a netcat session -# 6b2 start local netcat to dump tar to current dir -# 6b3 compare md5sums of dumped data with dump in current dir. if correct, contine, else restart the loop (6b1) -# 6c if i'm running as root: -# 6c1 create a temp dir using either tempdir command or the deviceid in /tmp -# 6c2 extract tar to tempdir -# 6c3 invoke mkyaffs2image to create the img -# 6c4 clean up -# 7. remove tools from device /cache -# 8. umount system and data on device -# 9. print success. - - -DEVICEID=foo -RECOVERY=foo - -echo "nandroid-mobile v2.1" - - -if [ "$1" == "" ]; then - echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" - echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" - echo "- restore path will restore the last made backup for boot, system, recovery and data" - exit 0 -fi - -case $1 in - backup) - mkyaffs2image=`which mkyaffs2image` - if [ "$mkyaffs2image" == "" ]; then - mkyaffs2image=`which mkyaffs2image-arm-uclibc` - if [ "$mkyaffs2image" == "" ]; then - echo "error: mkyaffs2image or mkyaffs2image-arm-uclibc not found in path" - exit 1 - fi - fi - dump_image=`which dump_image` - if [ "$dump_image" == "" ]; then - dump_image=`which dump_image-arm-uclibc` - if [ "$dump_image" == "" ]; then - echo "error: dump_image or dump_image-arm-uclibc not found in path" - exit 1 - fi - fi - break - ;; - restore) - flash_image=`which flash_image` - if [ "$flash_image" == "" ]; then - flash_image=`which flash_image-arm-uclibc` - if [ "$flash_image" == "" ]; then - echo "error: flash_image or flash_image-arm-uclibc not found in path" - exit 1 - fi - fi - break - ;; -esac - -# 1 -DEVICEID=`cat /proc/cmdline | sed "s/.*serialno=//" | cut -d" " -f1` -RECOVERY=`cat /proc/cmdline | grep "androidboot.mode=recovery"` -if [ "$RECOVERY" == "foo" ]; then - echo "error: not running in recovery mode, aborting" - exit 1 -fi -if [ "$DEVICEID" == "foo" ]; then - echo "error: device id not found in /proc/cmdline, aborting" - exit 1 -fi -if [ ! "`id -u 2>/dev/null`" == "0" ]; then - if [ "`whoami 2>&1 | grep 'uid 0'`" == "" ]; then - echo "error: must run as root, aborting" - exit 1 - fi -fi - - -case $1 in - restore) - ENERGY=`cat /sys/class/power_supply/battery/capacity` - if [ "`cat /sys/class/power_supply/battery/status`" == "Charging" ]; then - ENERGY=100 - fi - if [ ! $ENERGY -ge 30 ]; then - echo "Error: not enough battery power" - echo "Connect charger or USB power and try again" - exit 1 - fi - RESTOREPATH=$2 - if [ ! -f $RESTOREPATH/nandroid.md5 ]; then - echo "error: $RESTOREPATH/nandroid.md5 not found, cannot verify backup data" - exit 1 - fi - umount /system 2>/dev/null - umount /data 2>/dev/null - if [ ! "`mount | grep data`" == "" ]; then - echo "error: unable to umount /data, aborting" - exit 1 - fi - if [ ! "`mount | grep system`" == "" ]; then - echo "error: unable to umount /system, aborting" - exit 1 - fi - - echo "Verifying backup images..." - CWD=$PWD - cd $RESTOREPATH - md5sum -c nandroid.md5 - if [ $? -eq 1 ]; then - echo "error: md5sum mismatch, aborting" - exit 1 - fi - for image in boot recovery; do - echo "Flashing $image..." - $flash_image $image $image.img - done - echo "Flashing system and data not currently supported" - echo "Restore done" - exit 0 - ;; - backup) - break - ;; - *) - echo "Usage: $0 {backup|restore} [/path/to/nandroid/backup/]" - echo "- backup will store a full system backup on /sdcard/nandroid/$DEVICEID" - echo "- restore path will restore the last made backup for boot, system, recovery and data" - exit 1 - ;; -esac - -# 2. -echo "mounting system and data read-only, sdcard read-write" -umount /system 2>/dev/null -umount /data 2>/dev/null -umount /sdcard 2>/dev/null -mount -o ro /system || FAIL=1 -mount -o ro /data || FAIL=2 -mount /sdcard || mount /dev/block/mmcblk0 /sdcard || FAIL=3 -case $FAIL in - 1) echo "Error mounting system read-only"; umount /system /data /sdcard; exit 1;; - 2) echo "Error mounting data read-only"; umount /system /data /sdcard; exit 1;; - 3) echo "Error mounting sdcard read-write"; umount /system /data /sdcard; exit 1;; -esac - -TIMESTAMP="`date +%Y%m%d-%H%M`" -DESTDIR="/sdcard/nandroid/$DEVICEID/$TIMESTAMP" -if [ ! -d $DESTDIR ]; then - mkdir -p $DESTDIR - if [ ! -d $DESTDIR ]; then - echo "error: cannot create $DESTDIR" - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 - fi -else - touch $DESTDIR/.nandroidwritable - if [ ! -e $DESTDIR/.nandroidwritable ]; then - echo "error: cannot write to $DESTDIR" - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 - fi - rm $DESTDIR/.nandroidwritable -fi - -# 3. -echo "checking free space on sdcard" -FREEBLOCKS="`df -k /sdcard| grep sdcard | awk '{ print $4 }'`" -# we need about 130MB for the dump -if [ $FREEBLOCKS -le 130000 ]; then - echo "error: not enough free space available on sdcard (need 130mb), aborting." - umount /system 2>/dev/null - umount /data 2>/dev/null - umount /sdcard 2>/dev/null - exit 1 -fi - - - -if [ -e /dev/mtd/mtd6ro ]; then - echo -n "Dumping splash1 from device over tcp to $DESTDIR/splash1.img..." - dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash1.img skip=19072 bs=2048 count=150 2>/dev/null - echo "done" - sleep 1s - echo -n "Dumping splash2 from device over tcp to $DESTDIR/splash2.img..." - dd if=/dev/mtd/mtd6ro of=$DESTDIR/splash2.img skip=19456 bs=2048 count=150 2>/dev/null - echo "done" -fi - - -# 5. -for image in boot recovery misc; do - # 5a - DEVICEMD5=`$dump_image $image - | md5sum | awk '{ print $1 }'` - sleep 1s - MD5RESULT=1 - # 5b - echo -n "Dumping $image to $DESTDIR/$image.img..." - ATTEMPT=0 - while [ $MD5RESULT -eq 1 ]; do - let ATTEMPT=$ATTEMPT+1 - # 5b1 - $dump_image $image $DESTDIR/$image.img - sync - # 5b3 - echo "${DEVICEMD5} $DESTDIR/$image.img" | md5sum -c -s - - if [ $? -eq 1 ]; then - true - else - MD5RESULT=0 - fi - if [ "$ATTEMPT" == "5" ]; then - echo "fatal error while trying to dump $image, aborting" - umount /system - umount /data - umount /sdcard - exit 1 - fi - done - echo "done" -done - -# 6 -for image in system data cache; do - # 6a - echo -n "Dumping $image to $DESTDIR/$image.img..." - $mkyaffs2image /$image $DESTDIR/$image.img - sync - echo "done" -done - - -# 7. -echo -n "generating md5sum file..." -CWD=$PWD -cd $DESTDIR -md5sum *img > nandroid.md5 -cd $CWD -echo "done" - -# 8. -echo "unmounting system, data and sdcard" -umount /system -umount /data -umount /sdcard - -# 9. -echo "Backup successful."