Merge "Move PropertyMap from libutils to libinput"
diff --git a/fs_mgr/libsnapshot/Android.bp b/fs_mgr/libsnapshot/Android.bp
index 04d7e27..be6db04 100644
--- a/fs_mgr/libsnapshot/Android.bp
+++ b/fs_mgr/libsnapshot/Android.bp
@@ -387,6 +387,7 @@
         "cow_api_test.cpp",
     ],
     cflags: [
+        "-D_FILE_OFFSET_BITS=64",
         "-Wall",
         "-Werror",
     ],
@@ -409,6 +410,11 @@
     name: "make_cow_from_ab_ota",
     host_supported: true,
     device_supported: false,
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
     static_libs: [
         "libbase",
         "libbspatch",
@@ -436,3 +442,34 @@
         },
     },
 }
+
+cc_binary {
+    name: "estimate_cow_from_nonab_ota",
+    host_supported: true,
+    device_supported: false,
+    cflags: [
+        "-D_FILE_OFFSET_BITS=64",
+        "-Wall",
+        "-Werror",
+    ],
+    static_libs: [
+        "libbase",
+        "libbrotli",
+        "libbz",
+        "libcrypto",
+        "libgflags",
+        "liblog",
+        "libsnapshot_cow",
+        "libsparse",
+        "libz",
+        "libziparchive",
+    ],
+    srcs: [
+        "estimate_cow_from_nonab_ota.cpp",
+    ],
+    target: {
+        darwin: {
+            enabled: false,
+        },
+    },
+}
diff --git a/fs_mgr/libsnapshot/cow_api_test.cpp b/fs_mgr/libsnapshot/cow_api_test.cpp
index 3b3fc47..d98fe59 100644
--- a/fs_mgr/libsnapshot/cow_api_test.cpp
+++ b/fs_mgr/libsnapshot/cow_api_test.cpp
@@ -12,11 +12,15 @@
 // See the License for the specific language governing permissions and
 // limitations under the License.
 
+#include <sys/stat.h>
+
+#include <cstdio>
 #include <iostream>
 #include <memory>
 #include <string_view>
 
 #include <android-base/file.h>
+#include <android-base/logging.h>
 #include <gtest/gtest.h>
 #include <libsnapshot/cow_reader.h>
 #include <libsnapshot/cow_writer.h>
@@ -235,6 +239,34 @@
     ASSERT_EQ(sink.stream(), data);
 }
 
+TEST_F(CowTest, GetSize) {
+    CowOptions options;
+    CowWriter writer(options);
+    if (ftruncate(cow_->fd, 0) < 0) {
+        perror("Fails to set temp file size");
+        FAIL();
+    }
+    ASSERT_TRUE(writer.Initialize(cow_->fd));
+
+    std::string data = "This is some data, believe it";
+    data.resize(options.block_size, '\0');
+
+    ASSERT_TRUE(writer.AddCopy(10, 20));
+    ASSERT_TRUE(writer.AddRawBlocks(50, data.data(), data.size()));
+    ASSERT_TRUE(writer.AddZeroBlocks(51, 2));
+    auto size_before = writer.GetCowSize();
+    ASSERT_TRUE(writer.Finalize());
+    auto size_after = writer.GetCowSize();
+    ASSERT_EQ(size_before, size_after);
+    struct stat buf;
+
+    if (fstat(cow_->fd, &buf) < 0) {
+        perror("Fails to determine size of cow image written");
+        FAIL();
+    }
+    ASSERT_EQ(buf.st_size, writer.GetCowSize());
+}
+
 }  // namespace snapshot
 }  // namespace android
 
diff --git a/fs_mgr/libsnapshot/cow_writer.cpp b/fs_mgr/libsnapshot/cow_writer.cpp
index ea8e534..ff43997 100644
--- a/fs_mgr/libsnapshot/cow_writer.cpp
+++ b/fs_mgr/libsnapshot/cow_writer.cpp
@@ -21,6 +21,7 @@
 
 #include <android-base/file.h>
 #include <android-base/logging.h>
+#include <android-base/unique_fd.h>
 #include <libsnapshot/cow_writer.h>
 #include <openssl/sha.h>
 #include <zlib.h>
@@ -70,7 +71,7 @@
 
     // Headers are not complete, but this ensures the file is at the right
     // position.
-    if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
+    if (!WriteFully(fd_, &header_, sizeof(header_))) {
         PLOG(ERROR) << "write failed";
         return false;
     }
@@ -120,7 +121,7 @@
                 LOG(ERROR) << "Compressed block is too large: " << data.size() << " bytes";
                 return false;
             }
-            if (!android::base::WriteFully(fd_, data.data(), data.size())) {
+            if (!WriteFully(fd_, data.data(), data.size())) {
                 PLOG(ERROR) << "AddRawBlocks: write failed";
                 return false;
             }
@@ -136,7 +137,7 @@
         iter += header_.block_size;
     }
 
-    if (!compression_ && !android::base::WriteFully(fd_, data, size)) {
+    if (!compression_ && !WriteFully(fd_, data, size)) {
         PLOG(ERROR) << "AddRawBlocks: write failed";
         return false;
     }
@@ -186,6 +187,10 @@
 }
 
 bool CowWriter::Finalize() {
+    // If both fields are set then Finalize is already called.
+    if (header_.ops_offset > 0 && header_.ops_size > 0) {
+        return true;
+    }
     auto offs = lseek(fd_.get(), 0, SEEK_CUR);
     if (offs < 0) {
         PLOG(ERROR) << "lseek failed";
@@ -197,10 +202,12 @@
     SHA256(ops_.data(), ops_.size(), header_.ops_checksum);
     SHA256(&header_, sizeof(header_), header_.header_checksum);
 
-    if (lseek(fd_.get(), 0, SEEK_SET) < 0) {
-        PLOG(ERROR) << "lseek start failed";
+    if (lseek(fd_.get(), 0, SEEK_SET)) {
+        PLOG(ERROR) << "lseek failed";
         return false;
     }
+    // Header is already written, calling WriteFully will increment
+    // bytes_written_. So use android::base::WriteFully() here.
     if (!android::base::WriteFully(fd_, &header_, sizeof(header_))) {
         PLOG(ERROR) << "write header failed";
         return false;
@@ -209,13 +216,20 @@
         PLOG(ERROR) << "lseek ops failed";
         return false;
     }
-    if (!android::base::WriteFully(fd_, ops_.data(), ops_.size())) {
+    if (!WriteFully(fd_, ops_.data(), ops_.size())) {
         PLOG(ERROR) << "write ops failed";
         return false;
     }
+
+    // clear ops_ so that subsequent calls to GetSize() still works.
+    ops_.clear();
     return true;
 }
 
+size_t CowWriter::GetCowSize() {
+    return bytes_written_ + ops_.size() * sizeof(ops_[0]);
+}
+
 bool CowWriter::GetDataPos(uint64_t* pos) {
     off_t offs = lseek(fd_.get(), 0, SEEK_CUR);
     if (offs < 0) {
@@ -226,5 +240,10 @@
     return true;
 }
 
+bool CowWriter::WriteFully(base::borrowed_fd fd, const void* data, size_t size) {
+    bytes_written_ += size;
+    return android::base::WriteFully(fd, data, size);
+}
+
 }  // namespace snapshot
 }  // namespace android
diff --git a/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
new file mode 100644
index 0000000..45833e1
--- /dev/null
+++ b/fs_mgr/libsnapshot/estimate_cow_from_nonab_ota.cpp
@@ -0,0 +1,432 @@
+//
+// Copyright (C) 2020 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 <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <iostream>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/strings.h>
+#include <android-base/unique_fd.h>
+#include <gflags/gflags.h>
+#include <libsnapshot/cow_writer.h>
+#include <openssl/sha.h>
+#include <sparse/sparse.h>
+#include <ziparchive/zip_archive.h>
+
+DEFINE_string(source_tf, "", "Source target files (dir or zip file)");
+DEFINE_string(ota_tf, "", "Target files of the build for an OTA");
+DEFINE_string(compression, "gz", "Compression (options: none, gz, brotli)");
+
+namespace android {
+namespace snapshot {
+
+using android::base::borrowed_fd;
+using android::base::unique_fd;
+
+static constexpr size_t kBlockSize = 4096;
+
+void MyLogger(android::base::LogId, android::base::LogSeverity severity, const char*, const char*,
+              unsigned int, const char* message) {
+    if (severity == android::base::ERROR) {
+        fprintf(stderr, "%s\n", message);
+    } else {
+        fprintf(stdout, "%s\n", message);
+    }
+}
+
+class TargetFilesPackage final {
+  public:
+    explicit TargetFilesPackage(const std::string& path);
+
+    bool Open();
+    bool HasFile(const std::string& path);
+    std::unordered_set<std::string> GetDynamicPartitionNames();
+    unique_fd OpenFile(const std::string& path);
+    unique_fd OpenImage(const std::string& path);
+
+  private:
+    std::string path_;
+    unique_fd fd_;
+    std::unique_ptr<ZipArchive, decltype(&CloseArchive)> zip_;
+};
+
+TargetFilesPackage::TargetFilesPackage(const std::string& path)
+    : path_(path), zip_(nullptr, &CloseArchive) {}
+
+bool TargetFilesPackage::Open() {
+    fd_.reset(open(path_.c_str(), O_RDONLY));
+    if (fd_ < 0) {
+        PLOG(ERROR) << "open failed: " << path_;
+        return false;
+    }
+
+    struct stat s;
+    if (fstat(fd_.get(), &s) < 0) {
+        PLOG(ERROR) << "fstat failed: " << path_;
+        return false;
+    }
+    if (S_ISDIR(s.st_mode)) {
+        return true;
+    }
+
+    // Otherwise, assume it's a zip file.
+    ZipArchiveHandle handle;
+    if (OpenArchiveFd(fd_.get(), path_.c_str(), &handle, false)) {
+        LOG(ERROR) << "Could not open " << path_ << " as a zip archive.";
+        return false;
+    }
+    zip_.reset(handle);
+    return true;
+}
+
+bool TargetFilesPackage::HasFile(const std::string& path) {
+    if (zip_) {
+        ZipEntry64 entry;
+        return !FindEntry(zip_.get(), path, &entry);
+    }
+
+    auto full_path = path_ + "/" + path;
+    return access(full_path.c_str(), F_OK) == 0;
+}
+
+unique_fd TargetFilesPackage::OpenFile(const std::string& path) {
+    if (!zip_) {
+        auto full_path = path_ + "/" + path;
+        unique_fd fd(open(full_path.c_str(), O_RDONLY));
+        if (fd < 0) {
+            PLOG(ERROR) << "open failed: " << full_path;
+            return {};
+        }
+        return fd;
+    }
+
+    ZipEntry64 entry;
+    if (FindEntry(zip_.get(), path, &entry)) {
+        LOG(ERROR) << path << " not found in archive: " << path_;
+        return {};
+    }
+
+    TemporaryFile temp;
+    if (temp.fd < 0) {
+        PLOG(ERROR) << "mkstemp failed";
+        return {};
+    }
+
+    LOG(INFO) << "Extracting " << path << " from " << path_ << " ...";
+    if (ExtractEntryToFile(zip_.get(), &entry, temp.fd)) {
+        LOG(ERROR) << "could not extract " << path << " from " << path_;
+        return {};
+    }
+    if (lseek(temp.fd, 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return {};
+    }
+    return unique_fd{temp.release()};
+}
+
+unique_fd TargetFilesPackage::OpenImage(const std::string& path) {
+    auto fd = OpenFile(path);
+    if (fd < 0) {
+        return {};
+    }
+
+    LOG(INFO) << "Unsparsing " << path << " ...";
+    std::unique_ptr<struct sparse_file, decltype(&sparse_file_destroy)> s(
+            sparse_file_import(fd.get(), false, false), &sparse_file_destroy);
+    if (!s) {
+        return fd;
+    }
+
+    TemporaryFile temp;
+    if (temp.fd < 0) {
+        PLOG(ERROR) << "mkstemp failed";
+        return {};
+    }
+    if (sparse_file_write(s.get(), temp.fd, false, false, false) < 0) {
+        LOG(ERROR) << "sparse_file_write failed";
+        return {};
+    }
+    if (lseek(temp.fd, 0, SEEK_SET) < 0) {
+        PLOG(ERROR) << "lseek failed";
+        return {};
+    }
+
+    fd.reset(temp.release());
+    return fd;
+}
+
+std::unordered_set<std::string> TargetFilesPackage::GetDynamicPartitionNames() {
+    auto fd = OpenFile("META/misc_info.txt");
+    if (fd < 0) {
+        return {};
+    }
+
+    std::string contents;
+    if (!android::base::ReadFdToString(fd, &contents)) {
+        PLOG(ERROR) << "read failed";
+        return {};
+    }
+
+    std::unordered_set<std::string> set;
+
+    auto lines = android::base::Split(contents, "\n");
+    for (const auto& line : lines) {
+        auto parts = android::base::Split(line, "=");
+        if (parts.size() == 2 && parts[0] == "dynamic_partition_list") {
+            auto partitions = android::base::Split(parts[1], " ");
+            for (const auto& name : partitions) {
+                if (!name.empty()) {
+                    set.emplace(name);
+                }
+            }
+            break;
+        }
+    }
+    return set;
+}
+
+class NonAbEstimator final {
+  public:
+    NonAbEstimator(const std::string& ota_tf_path, const std::string& source_tf_path)
+        : ota_tf_path_(ota_tf_path), source_tf_path_(source_tf_path) {}
+
+    bool Run();
+
+  private:
+    bool OpenPackages();
+    bool AnalyzePartition(const std::string& partition_name);
+    std::unordered_map<std::string, uint64_t> GetBlockMap(borrowed_fd fd);
+
+    std::string ota_tf_path_;
+    std::string source_tf_path_;
+    std::unique_ptr<TargetFilesPackage> ota_tf_;
+    std::unique_ptr<TargetFilesPackage> source_tf_;
+    uint64_t size_ = 0;
+};
+
+bool NonAbEstimator::Run() {
+    if (!OpenPackages()) {
+        return false;
+    }
+
+    auto partitions = ota_tf_->GetDynamicPartitionNames();
+    if (partitions.empty()) {
+        LOG(ERROR) << "No dynamic partitions found in META/misc_info.txt";
+        return false;
+    }
+    for (const auto& partition : partitions) {
+        if (!AnalyzePartition(partition)) {
+            return false;
+        }
+    }
+
+    int64_t size_in_mb = int64_t(double(size_) / 1024.0 / 1024.0);
+
+    std::cout << "Estimated COW size: " << size_ << " (" << size_in_mb << "MiB)\n";
+    return true;
+}
+
+bool NonAbEstimator::OpenPackages() {
+    ota_tf_ = std::make_unique<TargetFilesPackage>(ota_tf_path_);
+    if (!ota_tf_->Open()) {
+        return false;
+    }
+    if (!source_tf_path_.empty()) {
+        source_tf_ = std::make_unique<TargetFilesPackage>(source_tf_path_);
+        if (!source_tf_->Open()) {
+            return false;
+        }
+    }
+    return true;
+}
+
+static std::string SHA256(const std::string& input) {
+    std::string hash(32, '\0');
+    SHA256_CTX c;
+    SHA256_Init(&c);
+    SHA256_Update(&c, input.data(), input.size());
+    SHA256_Final(reinterpret_cast<unsigned char*>(hash.data()), &c);
+    return hash;
+}
+
+bool NonAbEstimator::AnalyzePartition(const std::string& partition_name) {
+    auto path = "IMAGES/" + partition_name + ".img";
+    auto fd = ota_tf_->OpenImage(path);
+    if (fd < 0) {
+        return false;
+    }
+
+    unique_fd source_fd;
+    uint64_t source_size = 0;
+    std::unordered_map<std::string, uint64_t> source_blocks;
+    if (source_tf_) {
+        auto dap = source_tf_->GetDynamicPartitionNames();
+
+        source_fd = source_tf_->OpenImage(path);
+        if (source_fd >= 0) {
+            struct stat s;
+            if (fstat(source_fd.get(), &s)) {
+                PLOG(ERROR) << "fstat failed";
+                return false;
+            }
+            source_size = s.st_size;
+
+            std::cout << "Hashing blocks for " << partition_name << "...\n";
+            source_blocks = GetBlockMap(source_fd);
+            if (source_blocks.empty()) {
+                LOG(ERROR) << "Could not build a block map for source partition: "
+                           << partition_name;
+                return false;
+            }
+        } else {
+            if (dap.count(partition_name)) {
+                return false;
+            }
+            LOG(ERROR) << "Warning: " << partition_name
+                       << " has no incremental diff since it's not in the source image.";
+        }
+    }
+
+    TemporaryFile cow;
+    if (cow.fd < 0) {
+        PLOG(ERROR) << "mkstemp failed";
+        return false;
+    }
+
+    CowOptions options;
+    options.block_size = kBlockSize;
+    options.compression = FLAGS_compression;
+
+    auto writer = std::make_unique<CowWriter>(options);
+    if (!writer->Initialize(borrowed_fd{cow.fd})) {
+        LOG(ERROR) << "Could not initialize COW writer";
+        return false;
+    }
+
+    LOG(INFO) << "Analyzing " << partition_name << " ...";
+
+    std::string zeroes(kBlockSize, '\0');
+    std::string chunk(kBlockSize, '\0');
+    std::string src_chunk(kBlockSize, '\0');
+    uint64_t next_block_number = 0;
+    while (true) {
+        if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
+            if (errno) {
+                PLOG(ERROR) << "read failed";
+                return false;
+            }
+            break;
+        }
+
+        uint64_t block_number = next_block_number++;
+        if (chunk == zeroes) {
+            if (!writer->AddZeroBlocks(block_number, 1)) {
+                LOG(ERROR) << "Could not add zero block";
+                return false;
+            }
+            continue;
+        }
+
+        uint64_t source_offset = block_number * kBlockSize;
+        if (source_fd >= 0 && source_offset <= source_size) {
+            off64_t offset = block_number * kBlockSize;
+            if (android::base::ReadFullyAtOffset(source_fd, src_chunk.data(), src_chunk.size(),
+                                                 offset)) {
+                if (chunk == src_chunk) {
+                    continue;
+                }
+            } else if (errno) {
+                PLOG(ERROR) << "pread failed";
+                return false;
+            }
+        }
+
+        auto hash = SHA256(chunk);
+        if (auto iter = source_blocks.find(hash); iter != source_blocks.end()) {
+            if (!writer->AddCopy(block_number, iter->second)) {
+                return false;
+            }
+            continue;
+        }
+
+        if (!writer->AddRawBlocks(block_number, chunk.data(), chunk.size())) {
+            return false;
+        }
+    }
+
+    if (!writer->Finalize()) {
+        return false;
+    }
+
+    struct stat s;
+    if (fstat(cow.fd, &s) < 0) {
+        PLOG(ERROR) << "fstat failed";
+        return false;
+    }
+
+    size_ += s.st_size;
+    return true;
+}
+
+std::unordered_map<std::string, uint64_t> NonAbEstimator::GetBlockMap(borrowed_fd fd) {
+    std::string chunk(kBlockSize, '\0');
+
+    std::unordered_map<std::string, uint64_t> block_map;
+    uint64_t block_number = 0;
+    while (true) {
+        if (!android::base::ReadFully(fd, chunk.data(), chunk.size())) {
+            if (errno) {
+                PLOG(ERROR) << "read failed";
+                return {};
+            }
+            break;
+        }
+        auto hash = SHA256(chunk);
+        block_map[hash] = block_number;
+        block_number++;
+    }
+    return block_map;
+}
+
+}  // namespace snapshot
+}  // namespace android
+
+using namespace android::snapshot;
+
+int main(int argc, char** argv) {
+    android::base::InitLogging(argv, android::snapshot::MyLogger);
+    gflags::SetUsageMessage("Estimate VAB disk usage from Non A/B builds");
+    gflags::ParseCommandLineFlags(&argc, &argv, false);
+
+    if (FLAGS_ota_tf.empty()) {
+        std::cerr << "Must specify -ota_tf on the command-line." << std::endl;
+        return 1;
+    }
+
+    NonAbEstimator estimator(FLAGS_ota_tf, FLAGS_source_tf);
+    if (!estimator.Run()) {
+        return 1;
+    }
+    return 0;
+}
diff --git a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
index 5a2cbd6..8826b7a 100644
--- a/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
+++ b/fs_mgr/libsnapshot/include/libsnapshot/cow_writer.h
@@ -47,6 +47,14 @@
     // Encode a sequence of zeroed blocks. |size| must be a multiple of the block size.
     virtual bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) = 0;
 
+    // Finalize all COW operations and flush pending writes.
+    // Return true if successful.
+    virtual bool Finalize() = 0;
+
+    // Return 0 if failed, on success return number of bytes the cow image would be
+    // after calling Finalize();
+    virtual size_t GetCowSize() = 0;
+
   protected:
     CowOptions options_;
 };
@@ -63,23 +71,26 @@
     bool AddRawBlocks(uint64_t new_block_start, const void* data, size_t size) override;
     bool AddZeroBlocks(uint64_t new_block_start, uint64_t num_blocks) override;
 
-    // Finalize all COW operations and flush pending writes.
-    bool Finalize();
+    bool Finalize() override;
+
+    size_t GetCowSize() override;
 
   private:
     void SetupHeaders();
     bool GetDataPos(uint64_t* pos);
+    bool WriteFully(base::borrowed_fd fd, const void* data, size_t size);
     std::basic_string<uint8_t> Compress(const void* data, size_t length);
 
   private:
     android::base::unique_fd owned_fd_;
     android::base::borrowed_fd fd_;
-    CowHeader header_;
+    CowHeader header_{};
     int compression_ = 0;
 
     // :TODO: this is not efficient, but stringstream ubsan aborts because some
     // bytes overflow a signed char.
     std::basic_string<uint8_t> ops_;
+    std::atomic<size_t> bytes_written_ = 0;
 };
 
 }  // namespace snapshot
diff --git a/libcutils/ashmem-dev.cpp b/libcutils/ashmem-dev.cpp
index 233d400..6a27f9a 100644
--- a/libcutils/ashmem-dev.cpp
+++ b/libcutils/ashmem-dev.cpp
@@ -159,9 +159,11 @@
         return false;
     }
 
-    /* Check if kernel support exists, otherwise fall back to ashmem */
+    // Check if kernel support exists, otherwise fall back to ashmem.
+    // This code needs to build on old API levels, so we can't use the libc
+    // wrapper.
     android::base::unique_fd fd(
-            syscall(__NR_memfd_create, "test_android_memfd", MFD_ALLOW_SEALING));
+            syscall(__NR_memfd_create, "test_android_memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING));
     if (fd == -1) {
         ALOGE("memfd_create failed: %s, no memfd support.\n", strerror(errno));
         return false;
@@ -333,7 +335,9 @@
 }
 
 static int memfd_create_region(const char* name, size_t size) {
-    android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_ALLOW_SEALING));
+    // This code needs to build on old API levels, so we can't use the libc
+    // wrapper.
+    android::base::unique_fd fd(syscall(__NR_memfd_create, name, MFD_CLOEXEC | MFD_ALLOW_SEALING));
 
     if (fd == -1) {
         ALOGE("memfd_create(%s, %zd) failed: %s\n", name, size, strerror(errno));
diff --git a/libcutils/ashmem_test.cpp b/libcutils/ashmem_test.cpp
index b37d020..fb657f6 100644
--- a/libcutils/ashmem_test.cpp
+++ b/libcutils/ashmem_test.cpp
@@ -35,6 +35,11 @@
     ASSERT_TRUE(ashmem_valid(fd));
     ASSERT_EQ(size, static_cast<size_t>(ashmem_get_size_region(fd)));
     ASSERT_EQ(0, ashmem_set_prot_region(fd, prot));
+
+    // We've been inconsistent historically about whether or not these file
+    // descriptors were CLOEXEC. Make sure we're consistent going forward.
+    // https://issuetracker.google.com/165667331
+    ASSERT_EQ(FD_CLOEXEC, (fcntl(fd, F_GETFD) & FD_CLOEXEC));
 }
 
 void TestMmap(const unique_fd& fd, size_t size, int prot, void** region, off_t off = 0) {
diff --git a/libsparse/Android.bp b/libsparse/Android.bp
index 135904b..bf06bbc 100644
--- a/libsparse/Android.bp
+++ b/libsparse/Android.bp
@@ -6,6 +6,7 @@
     ramdisk_available: true,
     recovery_available: true,
     unique_host_soname: true,
+    vendor_available: true,
     srcs: [
         "backed_block.cpp",
         "output_file.cpp",
diff --git a/rootdir/init.rc b/rootdir/init.rc
index 44c8f27..faedd44 100644
--- a/rootdir/init.rc
+++ b/rootdir/init.rc
@@ -657,6 +657,15 @@
     mkdir /data/vendor/tombstones 0771 root root
     mkdir /data/vendor/tombstones/wifi 0771 wifi wifi
 
+    # Create directories to push tests to for each linker namespace.
+    # Create the subdirectories in case the first test is run as root
+    # so it doesn't end up owned by root.
+    mkdir /data/local/tests 0700 shell shell
+    mkdir /data/local/tests/product 0700 shell shell
+    mkdir /data/local/tests/system 0700 shell shell
+    mkdir /data/local/tests/unrestricted 0700 shell shell
+    mkdir /data/local/tests/vendor 0700 shell shell
+
     # create dalvik-cache, so as to enforce our permissions
     mkdir /data/dalvik-cache 0771 root root encryption=Require
     # create the A/B OTA directory, so as to enforce our permissions