Files
android_bootable_recovery/tests/unit/sysutil_test.cpp
Tao Bao 1700cc46b5 Fix the arguments passed to getopt_long(3).
The getopt_long(3) implementation in Android (upstream freebsd) expects
a null-terminated array while parsing long options with required args.

  if (long_options[match].has_arg == required_argument) {
    optarg = nargv[optind++];
  }
  ...
  if (long_options[match].has_arg == required_argument && optarg == NULL) {
    return (BADARG);
  }

This seems to make sense in practice, as getopt(3) takes the first two
arguments of argc and argv that are "as passed to the main() function on
program invocation", and both of C and C++ spec say "the value of
argv[argc] shall be 0".

Prior to the CL, we may run into undefined behavior on malformed input
command line (e.g. missing arg for an option that requires one). This CL
fixes the issue by always appending a nullptr to the argument list (but
without counting that into argc).

Test: Build and boot into recovery with commands.
Change-Id: Ic6c37548f4db2f30aeabd40f387ca916eeca5392
2018-07-17 12:16:53 -07:00

140 lines
5.0 KiB
C++

/*
* Copyright 2016 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 <string>
#include <android-base/file.h>
#include <android-base/test_utils.h>
#include <gtest/gtest.h>
#include "otautil/sysutil.h"
TEST(SysUtilTest, InvalidArgs) {
MemMapping mapping;
// Invalid argument.
ASSERT_FALSE(mapping.MapFile(""));
}
TEST(SysUtilTest, MapFileRegularFile) {
TemporaryFile temp_file1;
std::string content = "abc";
ASSERT_TRUE(android::base::WriteStringToFile(content, temp_file1.path));
// MemMapping::MapFile() should map the file to one range.
MemMapping mapping;
ASSERT_TRUE(mapping.MapFile(temp_file1.path));
ASSERT_NE(nullptr, mapping.addr);
ASSERT_EQ(content.size(), mapping.length);
ASSERT_EQ(1U, mapping.ranges());
}
TEST(SysUtilTest, MapFileBlockMap) {
// Create a file that has 10 blocks.
TemporaryFile package;
std::string content;
constexpr size_t file_size = 4096 * 10;
content.reserve(file_size);
ASSERT_TRUE(android::base::WriteStringToFile(content, package.path));
TemporaryFile block_map_file;
std::string filename = std::string("@") + block_map_file.path;
MemMapping mapping;
// One range.
std::string block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(1U, mapping.ranges());
// It's okay to not have the trailing '\n'.
block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(1U, mapping.ranges());
// Or having multiple trailing '\n's.
block_map_content = std::string(package.path) + "\n40960 4096\n1\n0 10\n\n\n";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(1U, mapping.ranges());
// Multiple ranges.
block_map_content = std::string(package.path) + "\n40960 4096\n3\n0 3\n3 5\n5 10\n";
ASSERT_TRUE(android::base::WriteStringToFile(block_map_content, block_map_file.path));
ASSERT_TRUE(mapping.MapFile(filename));
ASSERT_EQ(file_size, mapping.length);
ASSERT_EQ(3U, mapping.ranges());
}
TEST(SysUtilTest, MapFileBlockMapInvalidBlockMap) {
MemMapping mapping;
TemporaryFile temp_file;
std::string filename = std::string("@") + temp_file.path;
// Block map file is too short.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
// Block map file has unexpected number of lines.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n2\n0 1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
// Invalid size/blksize/range_count.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\nabc 4096\n1\n0 1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n\n0 1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
// size/blksize/range_count don't match.
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n0 4096\n1\n0 1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 0\n1\n0 1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
ASSERT_TRUE(android::base::WriteStringToFile("/somefile\n4096 4096\n0\n0 1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
// Invalid block dev path.
ASSERT_TRUE(android::base::WriteStringToFile("/doesntexist\n4096 4096\n1\n0 1\n", temp_file.path));
ASSERT_FALSE(mapping.MapFile(filename));
}
TEST(SysUtilTest, StringVectorToNullTerminatedArray) {
std::vector<std::string> args{ "foo", "bar", "baz" };
auto args_with_nullptr = StringVectorToNullTerminatedArray(args);
ASSERT_EQ(4, args_with_nullptr.size());
ASSERT_STREQ("foo", args_with_nullptr[0]);
ASSERT_STREQ("bar", args_with_nullptr[1]);
ASSERT_STREQ("baz", args_with_nullptr[2]);
ASSERT_EQ(nullptr, args_with_nullptr[3]);
}