Add sdcard support to cuttlefish

This adds support for formatting with vfat, and uses it to create an
sdcard.img file with embedded MBR and FAT32 partition data. The
partition defaults to 2GB but this can be configured.

This image persists across runs but it is not overlayed because it is
assumed that the initial contents of the file are not useful.

Bug: 156286088
Change-Id: Ie8f188a876f8940e26699cc7c5381265d723f0ec
Merged-In: Ie8f188a876f8940e26699cc7c5381265d723f0ec
diff --git a/host/commands/assemble_cvd/data_image.cc b/host/commands/assemble_cvd/data_image.cc
index a19d7dc..4e89ba6 100644
--- a/host/commands/assemble_cvd/data_image.cc
+++ b/host/commands/assemble_cvd/data_image.cc
@@ -2,9 +2,13 @@
 
 #include <glog/logging.h>
 
+#include "common/libs/fs/shared_buf.h"
+
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/subprocess.h"
 
+#include "host/commands/assemble_cvd/mbr.h"
+
 namespace {
 const std::string kDataPolicyUseExisting = "use_existing";
 const std::string kDataPolicyCreateIfMissing = "create_if_missing";
@@ -65,19 +69,53 @@
 void CreateBlankImage(
     const std::string& image, int num_mb, const std::string& image_fmt) {
   LOG(INFO) << "Creating " << image;
+
   off_t image_size_bytes = static_cast<off_t>(num_mb) << 20;
-  auto fd = cvd::SharedFD::Open(image, O_CREAT | O_TRUNC | O_RDWR, 0666);
-  if (fd->Truncate(image_size_bytes) != 0) {
-    LOG(ERROR) << "`truncate --size=" << num_mb << "M " << image
-               << "` failed:" << fd->StrError();
-    return;
+  // The newfs_msdos tool with the mandatory -C option will do the same
+  // as below to zero the image file, so we don't need to do it here
+  if (image_fmt != "sdcard") {
+    auto fd = cvd::SharedFD::Open(image, O_CREAT | O_TRUNC | O_RDWR, 0666);
+    if (fd->Truncate(image_size_bytes) != 0) {
+      LOG(ERROR) << "`truncate --size=" << num_mb << "M " << image
+                 << "` failed:" << fd->StrError();
+      return;
+    }
   }
-  fd->Close();
+
   if (image_fmt == "ext4") {
     cvd::execute({"/sbin/mkfs.ext4", image});
   } else if (image_fmt == "f2fs") {
     auto make_f2fs_path = vsoc::DefaultHostArtifactsPath("bin/make_f2fs");
     cvd::execute({make_f2fs_path, "-t", image_fmt, image, "-g", "android"});
+  } else if (image_fmt == "sdcard") {
+    // Reserve 1MB in the image for the MBR and padding, to simulate what
+    // other OSes do by default when partitioning a drive
+    off_t offset_size_bytes = 1 << 20;
+    image_size_bytes -= offset_size_bytes;
+    off_t image_size_sectors = image_size_bytes / 512;
+    auto newfs_msdos_path = vsoc::DefaultHostArtifactsPath("bin/newfs_msdos");
+    cvd::execute({newfs_msdos_path, "-F", "32", "-m", "0xf8", "-a", "4088",
+                                    "-o", "0",  "-c", "8",    "-h", "255",
+                                    "-u", "63", "-S", "512",
+                                    "-s", std::to_string(image_size_sectors),
+                                    "-C", std::to_string(num_mb) + "M",
+                                    "-@", std::to_string(offset_size_bytes),
+                                    image});
+    // Write the MBR after the filesystem is formatted, as the formatting tools
+    // don't consistently preserve the image contents
+    MasterBootRecord mbr = {
+      .partitions = {{
+        .partition_type = 0xC,
+        .first_lba = (std::uint32_t) offset_size_bytes / SECTOR_SIZE,
+        .num_sectors = (std::uint32_t) image_size_bytes / SECTOR_SIZE,
+      }},
+      .boot_signature = { 0x55, 0xAA },
+    };
+    auto fd = cvd::SharedFD::Open(image, O_RDWR);
+    if (cvd::WriteAllBinary(fd, &mbr) != sizeof(MasterBootRecord)) {
+      LOG(ERROR) << "Writing MBR to " << image << " failed:" << fd->StrError();
+      return;
+    }
   } else if (image_fmt != "none") {
     LOG(WARNING) << "Unknown image format '" << image_fmt
                  << "' for " << image << ", treating as 'none'.";
diff --git a/host/commands/assemble_cvd/flags.cc b/host/commands/assemble_cvd/flags.cc
index ee5543a..b16166c 100644
--- a/host/commands/assemble_cvd/flags.cc
+++ b/host/commands/assemble_cvd/flags.cc
@@ -34,6 +34,8 @@
               "to be generated.");
 DEFINE_int32(blank_metadata_image_mb, 16,
              "The size of the blank metadata image to generate, MB.");
+DEFINE_int32(blank_sdcard_image_mb, 2048,
+             "The size of the blank sdcard image to generate, MB.");
 DEFINE_int32(cpus, 2, "Virtual CPU count.");
 DEFINE_string(data_image, "", "Location of the data partition image.");
 DEFINE_string(data_policy, "use_existing", "How to handle userdata partition."
@@ -396,7 +398,10 @@
 
     instance.set_device_title(FLAGS_device_title);
 
-    instance.set_virtual_disk_paths({const_instance.PerInstancePath("overlay.img")});
+    instance.set_virtual_disk_paths({
+      const_instance.PerInstancePath("overlay.img"),
+      const_instance.sdcard_path(),
+    });
   }
 
   return tmp_config_obj;
@@ -742,6 +747,7 @@
       preserving.insert("gpt_header.img");
       preserving.insert("gpt_footer.img");
       preserving.insert("composite.img");
+      preserving.insert("sdcard.img");
       preserving.insert("access-kregistry");
     }
     if (!CleanPriorFiles(config, preserving)) {
@@ -873,6 +879,11 @@
     if (!cvd::FileExists(instance.access_kregistry_path())) {
       CreateBlankImage(instance.access_kregistry_path(), 2 /* mb */, "none");
     }
+
+    if (!cvd::FileExists(instance.sdcard_path())) {
+      CreateBlankImage(instance.sdcard_path(),
+                       FLAGS_blank_sdcard_image_mb, "sdcard");
+    }
   }
 
   if (SuperImageNeedsRebuilding(fetcher_config, *config)) {
diff --git a/host/commands/assemble_cvd/image_aggregator.cc b/host/commands/assemble_cvd/image_aggregator.cc
index eac6a2e..f700ccd 100644
--- a/host/commands/assemble_cvd/image_aggregator.cc
+++ b/host/commands/assemble_cvd/image_aggregator.cc
@@ -34,6 +34,7 @@
 #include "common/libs/fs/shared_fd.h"
 #include "common/libs/utils/files.h"
 #include "common/libs/utils/subprocess.h"
+#include "host/commands/assemble_cvd/mbr.h"
 #include "host/libs/config/cuttlefish_config.h"
 #include "device/google/cuttlefish/host/commands/assemble_cvd/cdisk_spec.pb.h"
 
diff --git a/host/commands/assemble_cvd/mbr.h b/host/commands/assemble_cvd/mbr.h
new file mode 100644
index 0000000..b06a5e3
--- /dev/null
+++ b/host/commands/assemble_cvd/mbr.h
@@ -0,0 +1,35 @@
+/*
+ * 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.
+ */
+#pragma once
+
+constexpr int SECTOR_SIZE = 512;
+
+struct __attribute__((packed)) MbrPartitionEntry {
+  std::uint8_t status;
+  std::uint8_t begin_chs[3];
+  std::uint8_t partition_type;
+  std::uint8_t end_chs[3];
+  std::uint32_t first_lba;
+  std::uint32_t num_sectors;
+};
+
+struct __attribute__((packed)) MasterBootRecord {
+  std::uint8_t bootstrap_code[446];
+  MbrPartitionEntry partitions[4];
+  std::uint8_t boot_signature[2];
+};
+
+static_assert(sizeof(MasterBootRecord) == SECTOR_SIZE);
diff --git a/host/libs/config/cuttlefish_config.cpp b/host/libs/config/cuttlefish_config.cpp
index 26b018b..ee3d67e 100644
--- a/host/libs/config/cuttlefish_config.cpp
+++ b/host/libs/config/cuttlefish_config.cpp
@@ -393,6 +393,10 @@
   return cvd::AbsolutePath(PerInstancePath("launcher.log"));
 }
 
+std::string CuttlefishConfig::InstanceSpecific::sdcard_path() const {
+  return cvd::AbsolutePath(PerInstancePath("sdcard.img"));
+}
+
 std::string CuttlefishConfig::InstanceSpecific::mobile_bridge_name() const {
   return (*Dictionary())[kMobileBridgeName].asString();
 }
diff --git a/host/libs/config/cuttlefish_config.h b/host/libs/config/cuttlefish_config.h
index 8209d81..005b1b2 100644
--- a/host/libs/config/cuttlefish_config.h
+++ b/host/libs/config/cuttlefish_config.h
@@ -320,6 +320,8 @@
     std::string launcher_log_path() const;
 
     std::string launcher_monitor_socket_path() const;
+
+    std::string sdcard_path() const;
   };
 
   // A view into an existing CuttlefishConfig object for a particular instance.
diff --git a/host_package.mk b/host_package.mk
index 0644114..8b9196f 100644
--- a/host_package.mk
+++ b/host_package.mk
@@ -56,7 +56,8 @@
     fsck.f2fs \
     resize.f2fs \
     make_f2fs \
-    tapsetiff
+    tapsetiff \
+    newfs_msdos \
 
 cvd_host_tests := \
     monotonic_time_test \
diff --git a/shared/sepolicy/vendor/genfs_contexts b/shared/sepolicy/vendor/genfs_contexts
index 82dea3d..14b8131 100644
--- a/shared/sepolicy/vendor/genfs_contexts
+++ b/shared/sepolicy/vendor/genfs_contexts
@@ -1,11 +1,11 @@
 # crosvm (x86)
-genfscon sysfs /devices/pci0000:00/0000:00:07.0/virtio6/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
-genfscon sysfs /devices/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # rmnet0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/subsystem_device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/subsystem_vendor u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/uevent u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/pci0000:00/0000:00:0a.0/vendor u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
+genfscon sysfs /devices/pci0000:00/0000:00:09.0/virtio8/net u:object_r:sysfs_net:s0 # rmnet0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/subsystem_device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/subsystem_vendor u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/uevent u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/pci0000:00/0000:00:0b.0/vendor u:object_r:sysfs_gpu:s0
 ## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
 ## x86 rtc_cmos on crosvm does not currently expose rtcN/hctosys
 ## find /sys/devices/platform/* -type d -name 'wakeup[0-9]'
@@ -16,13 +16,13 @@
 genfscon sysfs /devices/platform/rtc-test.2/wakeup/wakeup3 u:object_r:sysfs_wakeup:s0
 
 # crosvm (arm64)
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:07.0/virtio6/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # rmnet0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/subsystem_device u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/subsystem_vendor u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/uevent u:object_r:sysfs_gpu:s0
-genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0a.0/vendor u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:08.0/virtio7/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:09.0/virtio8/net u:object_r:sysfs_net:s0 # rmnet0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/subsystem_device u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/subsystem_vendor u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/uevent u:object_r:sysfs_gpu:s0
+genfscon sysfs /devices/platform/10000.pci/pci0000:00/0000:00:0b.0/vendor u:object_r:sysfs_gpu:s0
 ## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
 genfscon sysfs /devices/platform/2000.rtc/rtc u:object_r:sysfs_rtc:s0
 ## find /sys/devices/platform/* -type d -name 'wakeup[0-9]'
@@ -31,8 +31,8 @@
 genfscon sysfs /devices/platform/rtc-test.1/rtc/rtc2/alarmtimer.0.auto/wakeup/wakeup1 u:object_r:sysfs_wakeup:s0 # >5.5
 
 # qemu (x86)
-genfscon sysfs /devices/pci0000:00/0000:00:04.0/virtio2/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
-genfscon sysfs /devices/pci0000:00/0000:00:05.0/virtio3/net u:object_r:sysfs_net:s0 # rmnet0
+genfscon sysfs /devices/pci0000:00/0000:00:05.0/virtio3/net u:object_r:sysfs_net:s0 # buried_eth0 & wlan0
+genfscon sysfs /devices/pci0000:00/0000:00:06.0/virtio4/net u:object_r:sysfs_net:s0 # rmnet0
 # FIXME: Add sysfs_gpu labels for qemu
 ## find /sys/devices/platform/* -type d -name 'rtc[0-9]' | sed 's,/rtc[0-9],,'
 genfscon sysfs /devices/pnp0/00:00/rtc u:object_r:sysfs_rtc:s0