diff options
Diffstat (limited to 'fastboot')
-rw-r--r-- | fastboot/Android.mk | 4 | ||||
-rw-r--r-- | fastboot/engine.c | 48 | ||||
-rw-r--r-- | fastboot/fastboot.c | 101 | ||||
-rw-r--r-- | fastboot/fastboot.h | 4 | ||||
-rw-r--r-- | fastboot/usb.h | 1 | ||||
-rw-r--r-- | fastboot/usb_linux.c | 41 | ||||
-rw-r--r-- | fastboot/usb_osx.c | 8 | ||||
-rw-r--r-- | fastboot/usb_windows.c | 2 |
8 files changed, 183 insertions, 26 deletions
diff --git a/fastboot/Android.mk b/fastboot/Android.mk index 905f759..5025dae 100644 --- a/fastboot/Android.mk +++ b/fastboot/Android.mk @@ -57,15 +57,13 @@ LOCAL_STATIC_LIBRARIES := \ libz ifneq ($(HOST_OS),windows) -ifeq ($(HAVE_SELINUX), true) LOCAL_STATIC_LIBRARIES += libselinux -endif # HAVE_SELINUX endif # HOST_OS != windows include $(BUILD_HOST_EXECUTABLE) -$(call dist-for-goals,dist_files,$(LOCAL_BUILT_MODULE)) +$(call dist-for-goals,dist_files sdk,$(LOCAL_BUILT_MODULE)) ifeq ($(HOST_OS),linux) diff --git a/fastboot/engine.c b/fastboot/engine.c index 93be3de..8d46991 100644 --- a/fastboot/engine.c +++ b/fastboot/engine.c @@ -29,6 +29,7 @@ #include "fastboot.h" #include "make_ext4fs.h" +#include <errno.h> #include <stdio.h> #include <stdlib.h> #include <stdarg.h> @@ -45,8 +46,6 @@ #include <sys/mman.h> #endif -extern struct fs_info info; - #define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0])) double now() @@ -144,6 +143,39 @@ struct generator { { "ext4", generate_ext4_image, cleanup_image } }; +/* Return true if this partition is supported by the fastboot format command. + * It is also used to determine if we should first erase a partition before + * flashing it with an ext4 filesystem. See needs_erase() + * + * Not all devices report the filesystem type, so don't report any errors, + * just return false. + */ +int fb_format_supported(usb_handle *usb, const char *partition) +{ + char response[FB_RESPONSE_SZ+1]; + struct generator *generator = NULL; + int status; + unsigned int i; + + status = fb_getvar(usb, response, "partition-type:%s", partition); + if (status) { + return 0; + } + + for (i = 0; i < ARRAY_SIZE(generators); i++) { + if (!strncmp(generators[i].fs_type, response, FB_RESPONSE_SZ)) { + generator = &generators[i]; + break; + } + } + + if (generator) { + return 1; + } + + return 0; +} + static int cb_default(Action *a, int status, char *resp) { if (status) { @@ -269,10 +301,7 @@ void generate_ext4_image(struct image_data *image) #else fd = fileno(tmpfile()); #endif - /* reset ext4fs info so we can be called multiple times */ - reset_ext4fs_info(); - info.len = image->partition_size; - make_ext4fs_internal(fd, NULL, NULL, NULL, 0, 1, 0, 0, 0, NULL); + make_ext4fs_sparse_fd(fd, image->partition_size, NULL, NULL); fstat(fd, &st); image->image_size = st.st_size; @@ -565,6 +594,8 @@ int fb_execute_queue(usb_handle *usb) int status = 0; a = action_list; + if (!a) + return status; resp[FB_RESPONSE_SZ] = 0; double start = -1; @@ -605,3 +636,8 @@ int fb_execute_queue(usb_handle *usb) fprintf(stderr,"finished. total time: %.3fs\n", (now() - start)); return status; } + +int fb_queue_is_empty(void) +{ + return (action_list == NULL); +} diff --git a/fastboot/fastboot.c b/fastboot/fastboot.c index b6eab8c..3de6d7d 100644 --- a/fastboot/fastboot.c +++ b/fastboot/fastboot.c @@ -70,6 +70,7 @@ static const char *product = 0; static const char *cmdline = 0; static int wipe_data = 0; static unsigned short vendor_id = 0; +static int long_listing = 0; static int64_t sparse_limit = -1; static int64_t target_sparse_limit = -1; @@ -187,6 +188,11 @@ oops: int match_fastboot(usb_ifc_info *info) { + return match_fastboot_with_serial(info, serial); +} + +int match_fastboot_with_serial(usb_ifc_info *info, const char *local_serial) +{ if(!(vendor_id && (info->dev_vendor == vendor_id)) && (info->dev_vendor != 0x18d1) && // Google (info->dev_vendor != 0x8087) && // Intel @@ -204,15 +210,16 @@ int match_fastboot(usb_ifc_info *info) if(info->ifc_class != 0xff) return -1; if(info->ifc_subclass != 0x42) return -1; if(info->ifc_protocol != 0x03) return -1; - // require matching serial number if a serial number is specified + // require matching serial number or device path if requested // at the command line with the -s option. - if (serial && strcmp(serial, info->serial_number) != 0) return -1; + if (local_serial && (strcmp(local_serial, info->serial_number) != 0 && + strcmp(local_serial, info->device_path) != 0)) return -1; return 0; } int list_devices_callback(usb_ifc_info *info) { - if (match_fastboot(info) == 0) { + if (match_fastboot_with_serial(info, NULL) == 0) { char* serial = info->serial_number; if (!info->writable) { serial = "no permissions"; // like "adb devices" @@ -221,7 +228,13 @@ int list_devices_callback(usb_ifc_info *info) serial = "????????????"; } // output compatible with "adb devices" - printf("%s\tfastboot\n", serial); + if (!long_listing) { + printf("%s\tfastboot\n", serial); + } else if (!info->device_path) { + printf("%-22s fastboot\n", serial); + } else { + printf("%-22s fastboot %s\n", serial, info->device_path); + } } return -1; @@ -274,8 +287,13 @@ void usage(void) " help show this help message\n" "\n" "options:\n" - " -w erase userdata and cache\n" - " -s <serial number> specify device serial number\n" + " -w erase userdata and cache (and format\n" + " if supported by partition type)\n" + " -u do not first erase partition before\n" + " formatting\n" + " -s <specific device> specify device serial number\n" + " or path to device port\n" + " -l with \"devices\", lists device paths\n" " -p <product> specify product name\n" " -c <cmdline> override kernel commandline\n" " -i <vendor id> specify a custom USB vendor id\n" @@ -547,6 +565,18 @@ static int64_t get_sparse_limit(struct usb_handle *usb, int64_t size) return 0; } +/* Until we get lazy inode table init working in make_ext4fs, we need to + * erase partitions of type ext4 before flashing a filesystem so no stale + * inodes are left lying around. Otherwise, e2fsck gets very upset. + */ +static int needs_erase(const char *part) +{ + /* The function fb_format_supported() currently returns the value + * we want, so just call it. + */ + return fb_format_supported(usb, part); +} + void do_flash(usb_handle *usb, const char *pname, const char *fname) { int64_t sz64; @@ -582,7 +612,7 @@ void do_update_signature(zipfile_t zip, char *fn) fb_queue_command("signature", "installing signature"); } -void do_update(char *fn) +void do_update(char *fn, int erase_first) { void *zdata; unsigned zsize; @@ -620,17 +650,26 @@ void do_update(char *fn) data = unzip_file(zip, "boot.img", &sz); if (data == 0) die("update package missing boot.img"); do_update_signature(zip, "boot.sig"); + if (erase_first && needs_erase("boot")) { + fb_queue_erase("boot"); + } fb_queue_flash("boot", data, sz); data = unzip_file(zip, "recovery.img", &sz); if (data != 0) { do_update_signature(zip, "recovery.sig"); + if (erase_first && needs_erase("recovery")) { + fb_queue_erase("recovery"); + } fb_queue_flash("recovery", data, sz); } data = unzip_file(zip, "system.img", &sz); if (data == 0) die("update package missing system.img"); do_update_signature(zip, "system.sig"); + if (erase_first && needs_erase("system")) { + fb_queue_erase("system"); + } fb_queue_flash("system", data, sz); } @@ -652,7 +691,7 @@ void do_send_signature(char *fn) fb_queue_command("signature", "installing signature"); } -void do_flashall(void) +void do_flashall(int erase_first) { char *fname; void *data; @@ -672,12 +711,18 @@ void do_flashall(void) data = load_file(fname, &sz); if (data == 0) die("could not load boot.img: %s", strerror(errno)); do_send_signature(fname); + if (erase_first && needs_erase("boot")) { + fb_queue_erase("boot"); + } fb_queue_flash("boot", data, sz); fname = find_item("recovery", product); data = load_file(fname, &sz); if (data != 0) { do_send_signature(fname); + if (erase_first && needs_erase("recovery")) { + fb_queue_erase("recovery"); + } fb_queue_flash("recovery", data, sz); } @@ -685,6 +730,9 @@ void do_flashall(void) data = load_file(fname, &sz); if (data == 0) die("could not load system.img: %s", strerror(errno)); do_send_signature(fname); + if (erase_first && needs_erase("system")) { + fb_queue_erase("system"); + } fb_queue_flash("system", data, sz); } @@ -755,6 +803,7 @@ int main(int argc, char **argv) int wants_wipe = 0; int wants_reboot = 0; int wants_reboot_bootloader = 0; + int erase_first = 1; void *data; unsigned sz; unsigned page_size = 2048; @@ -767,7 +816,7 @@ int main(int argc, char **argv) serial = getenv("ANDROID_SERIAL"); while (1) { - c = getopt_long(argc, argv, "wb:n:s:S:p:c:i:m:h", &longopts, NULL); + c = getopt_long(argc, argv, "wub:n:s:S:lp:c:i:m:h", &longopts, NULL); if (c < 0) { break; } @@ -776,6 +825,9 @@ int main(int argc, char **argv) case 'w': wants_wipe = 1; break; + case 'u': + erase_first = 0; + break; case 'b': base_addr = strtoul(optarg, 0, 16); break; @@ -792,6 +844,9 @@ int main(int argc, char **argv) die("invalid sparse limit"); } break; + case 'l': + long_listing = 1; + break; case 'p': product = optarg; break; @@ -832,6 +887,11 @@ int main(int argc, char **argv) return 0; } + if (argc > 0 && !strcmp(*argv, "help")) { + usage(); + return 0; + } + usb = open_device(); while (argc > 0) { @@ -841,10 +901,18 @@ int main(int argc, char **argv) skip(2); } else if(!strcmp(*argv, "erase")) { require(2); + + if (fb_format_supported(usb, argv[1])) { + fprintf(stderr, "******** Did you mean to fastboot format this partition?\n"); + } + fb_queue_erase(argv[1]); skip(2); } else if(!strcmp(*argv, "format")) { require(2); + if (erase_first && needs_erase(argv[1])) { + fb_queue_erase(argv[1]); + } fb_queue_format(argv[1], 0); skip(2); } else if(!strcmp(*argv, "signature")) { @@ -892,6 +960,9 @@ int main(int argc, char **argv) skip(2); } if (fname == 0) die("cannot determine image filename for '%s'", pname); + if (erase_first && needs_erase(pname)) { + fb_queue_erase(pname); + } do_flash(usb, pname, fname); } else if(!strcmp(*argv, "flash:raw")) { char *pname = argv[1]; @@ -909,22 +980,19 @@ int main(int argc, char **argv) fb_queue_flash(pname, data, sz); } else if(!strcmp(*argv, "flashall")) { skip(1); - do_flashall(); + do_flashall(erase_first); wants_reboot = 1; } else if(!strcmp(*argv, "update")) { if (argc > 1) { - do_update(argv[1]); + do_update(argv[1], erase_first); skip(2); } else { - do_update("update.zip"); + do_update("update.zip", erase_first); skip(1); } wants_reboot = 1; } else if(!strcmp(*argv, "oem")) { argc = do_oem_command(argc, argv); - } else if (!strcmp(*argv, "help")) { - usage(); - return 0; } else { usage(); return 1; @@ -943,6 +1011,9 @@ int main(int argc, char **argv) fb_queue_command("reboot-bootloader", "rebooting into bootloader"); } + if (fb_queue_is_empty()) + return 0; + status = fb_execute_queue(usb); return (status) ? 1 : 0; } diff --git a/fastboot/fastboot.h b/fastboot/fastboot.h index 9177932..c1b2964 100644 --- a/fastboot/fastboot.h +++ b/fastboot/fastboot.h @@ -45,7 +45,8 @@ char *fb_get_error(void); /* engine.c - high level command queue engine */ int fb_getvar(struct usb_handle *usb, char *response, const char *fmt, ...); -void fb_queue_flash(const char *ptn, void *data, unsigned sz);; +int fb_format_supported(usb_handle *usb, const char *partition); +void fb_queue_flash(const char *ptn, void *data, unsigned sz); void fb_queue_flash_sparse(const char *ptn, struct sparse_file *s, unsigned sz); void fb_queue_erase(const char *ptn); void fb_queue_format(const char *ptn, int skip_if_not_supported); @@ -58,6 +59,7 @@ void fb_queue_command(const char *cmd, const char *msg); void fb_queue_download(const char *name, void *data, unsigned size); void fb_queue_notice(const char *notice); int fb_execute_queue(usb_handle *usb); +int fb_queue_is_empty(void); /* util stuff */ void die(const char *fmt, ...); diff --git a/fastboot/usb.h b/fastboot/usb.h index df9efde..d504ee2 100644 --- a/fastboot/usb.h +++ b/fastboot/usb.h @@ -53,6 +53,7 @@ struct usb_ifc_info unsigned char writable; char serial_number[256]; + char device_path[256]; }; typedef int (*ifc_match_func)(usb_ifc_info *ifc); diff --git a/fastboot/usb_linux.c b/fastboot/usb_linux.c index 83c6de9..b7a9ca3 100644 --- a/fastboot/usb_linux.c +++ b/fastboot/usb_linux.c @@ -32,6 +32,7 @@ #include <string.h> #include <sys/ioctl.h> +#include <sys/stat.h> #include <sys/types.h> #include <dirent.h> #include <fcntl.h> @@ -107,6 +108,9 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, int in, out; unsigned i; unsigned e; + + struct stat st; + int result; if(check(ptr, len, USB_DT_DEVICE, USB_DT_DEVICE_SIZE)) return -1; @@ -134,7 +138,6 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, // Keep it short enough because some bootloaders are borked if the URB len is > 255 // 128 is too big by 1. __u16 buffer[127]; - int result; memset(buffer, 0, sizeof(buffer)); @@ -158,6 +161,42 @@ static int filter_usb_device(int fd, char *ptr, int len, int writable, } } + /* We need to get a path that represents a particular port on a particular + * hub. We are passed an fd that was obtained by opening an entry under + * /dev/bus/usb. Unfortunately, the names of those entries change each + * time devices are plugged and unplugged. So how to get a repeatable + * path? udevadm provided the inspiration. We can get the major and + * minor of the device file, read the symlink that can be found here: + * /sys/dev/char/<major>:<minor> + * and then use the last element of that path. As a concrete example, I + * have an Android device at /dev/bus/usb/001/027 so working with bash: + * $ ls -l /dev/bus/usb/001/027 + * crw-rw-r-- 1 root plugdev 189, 26 Apr 9 11:03 /dev/bus/usb/001/027 + * $ ls -l /sys/dev/char/189:26 + * lrwxrwxrwx 1 root root 0 Apr 9 11:03 /sys/dev/char/189:26 -> + * ../../devices/pci0000:00/0000:00:1a.7/usb1/1-4/1-4.2/1-4.2.3 + * So our device_path would be 1-4.2.3 which says my device is connected + * to port 3 of a hub on port 2 of a hub on port 4 of bus 1 (per + * http://www.linux-usb.org/FAQ.html). + */ + info.device_path[0] = '\0'; + result = fstat(fd, &st); + if (!result && S_ISCHR(st.st_mode)) { + char cdev[128]; + char link[256]; + char *slash; + ssize_t link_len; + snprintf(cdev, sizeof(cdev), "/sys/dev/char/%d:%d", + major(st.st_rdev), minor(st.st_rdev)); + link_len = readlink(cdev, link, sizeof(link) - 1); + if (link_len > 0) { + link[link_len] = '\0'; + slash = strrchr(link, '/'); + if (slash) + snprintf(info.device_path, sizeof(info.device_path), "usb:%s", slash+1); + } + } + for(i = 0; i < cfg->bNumInterfaces; i++) { if(check(ptr, len, USB_DT_INTERFACE, USB_DT_INTERFACE_SIZE)) return -1; diff --git a/fastboot/usb_osx.c b/fastboot/usb_osx.c index cbce9bd..1548ba8 100644 --- a/fastboot/usb_osx.c +++ b/fastboot/usb_osx.c @@ -264,6 +264,7 @@ static int try_device(io_service_t device, usb_handle *handle) { SInt32 score; HRESULT result; UInt8 serialIndex; + UInt32 locationId; // Create an intermediate plugin. kr = IOCreatePlugInInterfaceForService(device, @@ -322,6 +323,13 @@ static int try_device(io_service_t device, usb_handle *handle) { goto error; } + kr = (*dev)->GetLocationID(dev, &locationId); + if (kr != 0) { + ERR("GetLocationId"); + goto error; + } + snprintf(handle->info.device_path, sizeof(handle->info.device_path), "usb:%lX", locationId); + kr = (*dev)->USBGetSerialNumberStringIndex(dev, &serialIndex); if (serialIndex > 0) { diff --git a/fastboot/usb_windows.c b/fastboot/usb_windows.c index 99027cc..7aa36b2 100644 --- a/fastboot/usb_windows.c +++ b/fastboot/usb_windows.c @@ -311,6 +311,8 @@ int recognized_device(usb_handle* handle, ifc_match_func callback) { info.serial_number[0] = 0; } + info.device_path[0] = 0; + if (callback(&info) == 0) { return 1; } |