diff --git a/base/include/android-base/parseint.h b/base/include/android-base/parseint.h index 2c8570ead..1b7cc5fa6 100644 --- a/base/include/android-base/parseint.h +++ b/base/include/android-base/parseint.h @@ -19,6 +19,7 @@ #include #include +#include #include #include @@ -31,14 +32,20 @@ namespace base { // otherwise valid values will be rejected. Returns boolean success; 'out' // is untouched if parsing fails. template -bool ParseUint(const char* s, T* out, - T max = std::numeric_limits::max()) { +bool ParseUint(const char* s, T* out, T max = std::numeric_limits::max(), + bool allow_suffixes = false) { int base = (s[0] == '0' && (s[1] == 'x' || s[1] == 'X')) ? 16 : 10; errno = 0; char* end; unsigned long long int result = strtoull(s, &end, base); - if (errno != 0 || s == end || *end != '\0') { - return false; + if (errno != 0 || end == s) return false; + if (*end != '\0') { + const char* suffixes = "bkmgtpe"; + const char* suffix; + if (!allow_suffixes || (suffix = strchr(suffixes, tolower(*end))) == nullptr) return false; +#if __clang__ // TODO: win32 still builds with GCC :-( + if (__builtin_mul_overflow(result, 1ULL << (10 * (suffix - suffixes)), &result)) return false; +#endif } if (max < result) { return false; @@ -49,9 +56,20 @@ bool ParseUint(const char* s, T* out, // TODO: string_view template -bool ParseUint(const std::string& s, T* out, - T max = std::numeric_limits::max()) { - return ParseUint(s.c_str(), out, max); +bool ParseUint(const std::string& s, T* out, T max = std::numeric_limits::max(), + bool allow_suffixes = false) { + return ParseUint(s.c_str(), out, max, allow_suffixes); +} + +template +bool ParseByteCount(const char* s, T* out, T max = std::numeric_limits::max()) { + return ParseUint(s, out, max, true); +} + +// TODO: string_view +template +bool ParseByteCount(const std::string& s, T* out, T max = std::numeric_limits::max()) { + return ParseByteCount(s.c_str(), out, max); } // Parses the signed decimal integer in the string 's' and sets 'out' to diff --git a/base/parseint_test.cpp b/base/parseint_test.cpp index 483b1d3be..fb1c33993 100644 --- a/base/parseint_test.cpp +++ b/base/parseint_test.cpp @@ -96,3 +96,44 @@ TEST(parseint, untouched_on_failure) { ASSERT_FALSE(android::base::ParseInt("456x", &u)); ASSERT_EQ(123u, u); } + +TEST(parseint, ParseByteCount) { + uint64_t i = 0; + ASSERT_TRUE(android::base::ParseByteCount("123b", &i)); + ASSERT_EQ(123ULL, i); + + ASSERT_TRUE(android::base::ParseByteCount("8k", &i)); + ASSERT_EQ(8ULL * 1024, i); + + ASSERT_TRUE(android::base::ParseByteCount("8M", &i)); + ASSERT_EQ(8ULL * 1024 * 1024, i); + + ASSERT_TRUE(android::base::ParseByteCount("6g", &i)); + ASSERT_EQ(6ULL * 1024 * 1024 * 1024, i); + + ASSERT_TRUE(android::base::ParseByteCount("1T", &i)); + ASSERT_EQ(1ULL * 1024 * 1024 * 1024 * 1024, i); + + ASSERT_TRUE(android::base::ParseByteCount("2p", &i)); + ASSERT_EQ(2ULL * 1024 * 1024 * 1024 * 1024 * 1024, i); + + ASSERT_TRUE(android::base::ParseByteCount("4e", &i)); + ASSERT_EQ(4ULL * 1024 * 1024 * 1024 * 1024 * 1024 * 1024, i); +} + +TEST(parseint, ParseByteCount_invalid_suffix) { + unsigned u; + ASSERT_FALSE(android::base::ParseByteCount("1x", &u)); +} + +TEST(parseint, ParseByteCount_overflow) { + uint64_t u64; + ASSERT_FALSE(android::base::ParseByteCount("4294967295E", &u64)); + + uint16_t u16; + ASSERT_TRUE(android::base::ParseByteCount("63k", &u16)); + ASSERT_EQ(63U * 1024, u16); + ASSERT_TRUE(android::base::ParseByteCount("65535b", &u16)); + ASSERT_EQ(65535U, u16); + ASSERT_FALSE(android::base::ParseByteCount("65k", &u16)); +} diff --git a/fastboot/fastboot.cpp b/fastboot/fastboot.cpp index 41a3d6b44..780ff50db 100644 --- a/fastboot/fastboot.cpp +++ b/fastboot/fastboot.cpp @@ -82,7 +82,7 @@ static bool g_long_listing = false; // libsparse will support INT_MAX, but this results in large allocations, so // let's keep it at 1GB to avoid memory pressure on the host. static constexpr int64_t RESPARSE_LIMIT = 1 * 1024 * 1024 * 1024; -static int64_t sparse_limit = -1; +static uint64_t sparse_limit = 0; static int64_t target_sparse_limit = -1; static unsigned g_base_addr = 0x10000000; @@ -375,7 +375,7 @@ static int show_help() { " -w Wipe userdata.\n" " -s SERIAL Specify a USB device.\n" " -s tcp|udp:HOST[:PORT] Specify a network device.\n" - " -S SIZE[K|M|G] Use sparse files above this limit (0 to disable).\n" + " -S SIZE[K|M|G] Break into sparse files no larger than SIZE.\n" " --slot SLOT Use SLOT; 'all' for both slots, 'other' for\n" " non-current slot (default: current active slot).\n" " --set-active[=SLOT] Sets the active slot before rebooting.\n" @@ -730,13 +730,10 @@ static int64_t get_target_sparse_limit(Transport* transport) { } static int64_t get_sparse_limit(Transport* transport, int64_t size) { - int64_t limit; - - if (sparse_limit == 0) { - return 0; - } else if (sparse_limit > 0) { - limit = sparse_limit; - } else { + int64_t limit = sparse_limit; + if (limit == 0) { + // Unlimited, so see what the target device's limit is. + // TODO: shouldn't we apply this limit even if you've used -S? if (target_sparse_limit == -1) { target_sparse_limit = get_target_sparse_limit(transport); } @@ -1197,47 +1194,6 @@ static void do_oem_command(const std::string& cmd, std::vector* arg fb_queue_command(command, ""); } -static int64_t parse_num(const char *arg) -{ - char *endptr; - unsigned long long num; - - num = strtoull(arg, &endptr, 0); - if (endptr == arg) { - return -1; - } - - if (*endptr == 'k' || *endptr == 'K') { - if (num >= (-1ULL) / 1024) { - return -1; - } - num *= 1024LL; - endptr++; - } else if (*endptr == 'm' || *endptr == 'M') { - if (num >= (-1ULL) / (1024 * 1024)) { - return -1; - } - num *= 1024LL * 1024LL; - endptr++; - } else if (*endptr == 'g' || *endptr == 'G') { - if (num >= (-1ULL) / (1024 * 1024 * 1024)) { - return -1; - } - num *= 1024LL * 1024LL * 1024LL; - endptr++; - } - - if (*endptr != '\0') { - return -1; - } - - if (num > INT64_MAX) { - return -1; - } - - return num; -} - static std::string fb_fix_numeric_var(std::string var) { // Some bootloaders (angler, for example), send spurious leading whitespace. var = android::base::Trim(var); @@ -1471,8 +1427,9 @@ int FastBoot::Main(int argc, char* argv[]) { serial = optarg; break; case 'S': - sparse_limit = parse_num(optarg); - if (sparse_limit < 0) die("invalid sparse limit"); + if (!android::base::ParseByteCount(optarg, &sparse_limit)) { + die("invalid sparse limit %s", optarg); + } break; case 'v': set_verbose();