Merge "Revert "libcutils: fs_config: target_out_path assumed /system""
diff --git a/base/Android.bp b/base/Android.bp
index 121da42..3af7686 100644
--- a/base/Android.bp
+++ b/base/Android.bp
@@ -98,6 +98,7 @@
         "parseint_test.cpp",
         "parsenetaddress_test.cpp",
         "quick_exit_test.cpp",
+        "scopeguard_test.cpp",
         "stringprintf_test.cpp",
         "strings_test.cpp",
         "test_main.cpp",
diff --git a/base/file.cpp b/base/file.cpp
index d4e5894..7fbebc5 100644
--- a/base/file.cpp
+++ b/base/file.cpp
@@ -28,8 +28,9 @@
 #include <string>
 #include <vector>
 
-#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
 #include "android-base/logging.h"
+#include "android-base/macros.h"  // For TEMP_FAILURE_RETRY on Darwin.
+#include "android-base/unique_fd.h"
 #include "android-base/utf8.h"
 #include "utils/Compat.h"
 
@@ -69,13 +70,11 @@
   content->clear();
 
   int flags = O_RDONLY | O_CLOEXEC | O_BINARY | (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags)));
   if (fd == -1) {
     return false;
   }
-  bool result = ReadFdToString(fd, content);
-  close(fd);
-  return result;
+  return ReadFdToString(fd, content);
 }
 
 bool WriteStringToFd(const std::string& content, int fd) {
@@ -106,7 +105,7 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, mode)));
   if (fd == -1) {
     PLOG(ERROR) << "android::WriteStringToFile open failed";
     return false;
@@ -126,7 +125,6 @@
     PLOG(ERROR) << "android::WriteStringToFile write failed";
     return CleanUpAfterFailedWrite(path);
   }
-  close(fd);
   return true;
 }
 #endif
@@ -135,14 +133,11 @@
                        bool follow_symlinks) {
   int flags = O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC | O_BINARY |
               (follow_symlinks ? 0 : O_NOFOLLOW);
-  int fd = TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE));
+  android::base::unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), flags, DEFFILEMODE)));
   if (fd == -1) {
     return false;
   }
-
-  bool result = WriteStringToFd(content, fd);
-  close(fd);
-  return result || CleanUpAfterFailedWrite(path);
+  return WriteStringToFd(content, fd) || CleanUpAfterFailedWrite(path);
 }
 
 bool ReadFully(int fd, void* data, size_t byte_count) {
diff --git a/base/include/android-base/scopeguard.h b/base/include/android-base/scopeguard.h
new file mode 100644
index 0000000..abcf4bc
--- /dev/null
+++ b/base/include/android-base/scopeguard.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+#ifndef ANDROID_BASE_SCOPEGUARD_H
+#define ANDROID_BASE_SCOPEGUARD_H
+
+#include <utility>  // for std::move
+
+namespace android {
+namespace base {
+
+template <typename F>
+class ScopeGuard {
+ public:
+  ScopeGuard(F f) : f_(f), active_(true) {}
+
+  ScopeGuard(ScopeGuard&& that) : f_(std::move(that.f_)), active_(that.active_) {
+    that.active_ = false;
+  }
+
+  ~ScopeGuard() {
+    if (active_) f_();
+  }
+
+  ScopeGuard() = delete;
+  ScopeGuard(const ScopeGuard&) = delete;
+  void operator=(const ScopeGuard&) = delete;
+  void operator=(ScopeGuard&& that) = delete;
+
+  void Disable() { active_ = false; }
+
+  bool active() const { return active_; }
+
+ private:
+  F f_;
+  bool active_;
+};
+
+template <typename T>
+ScopeGuard<T> make_scope_guard(T f) {
+  return ScopeGuard<T>(f);
+}
+
+}  // namespace base
+}  // namespace android
+
+#endif  // ANDROID_BASE_SCOPEGUARD_H
diff --git a/base/scopeguard_test.cpp b/base/scopeguard_test.cpp
new file mode 100644
index 0000000..e11154a
--- /dev/null
+++ b/base/scopeguard_test.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2017 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 "android-base/scopeguard.h"
+
+#include <utility>
+
+#include <gtest/gtest.h>
+
+TEST(scopeguard, normal) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  }
+  ASSERT_FALSE(guarded_var);
+}
+
+TEST(scopeguard, disabled) {
+  bool guarded_var = true;
+  {
+    auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+    scopeguard.Disable();
+  }
+  ASSERT_TRUE(guarded_var);
+}
+
+TEST(scopeguard, moved) {
+  int guarded_var = true;
+  auto scopeguard = android::base::make_scope_guard([&guarded_var] { guarded_var = false; });
+  { decltype(scopeguard) new_guard(std::move(scopeguard)); }
+  EXPECT_FALSE(scopeguard.active());
+  ASSERT_FALSE(guarded_var);
+}
diff --git a/bootstat/Android.bp b/bootstat/Android.bp
index 95c9af5..bc90a6e 100644
--- a/bootstat/Android.bp
+++ b/bootstat/Android.bp
@@ -74,6 +74,7 @@
 // -----------------------------------------------------------------------------
 cc_test {
     name: "bootstat_tests",
+    test_suites: ["device-tests"],
     defaults: ["bootstat_defaults"],
     host_supported: true,
     static_libs: [
diff --git a/bootstat/AndroidTest.xml b/bootstat/AndroidTest.xml
new file mode 100644
index 0000000..f3783fa
--- /dev/null
+++ b/bootstat/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2017 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.
+-->
+<configuration description="Config for bootstat_tests">
+    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+        <option name="cleanup" value="true" />
+        <option name="push" value="bootstat_tests->/data/local/tmp/bootstat_tests" />
+    </target_preparer>
+    <option name="test-suite-tag" value="apct" />
+    <test class="com.android.tradefed.testtype.GTest" >
+        <option name="native-test-device-path" value="/data/local/tmp" />
+        <option name="module-name" value="bootstat_tests" />
+    </test>
+</configuration>
\ No newline at end of file
diff --git a/debuggerd/client/debuggerd_client.cpp b/debuggerd/client/debuggerd_client.cpp
index 3b84853..2be13c6 100644
--- a/debuggerd/client/debuggerd_client.cpp
+++ b/debuggerd/client/debuggerd_client.cpp
@@ -140,7 +140,9 @@
   }
 
   bool backtrace = dump_type == kDebuggerdBacktrace;
-  send_signal(pid, backtrace);
+  if (!send_signal(pid, backtrace)) {
+    return false;
+  }
 
   rc = TEMP_FAILURE_RETRY(recv(set_timeout(sockfd.get()), &response, sizeof(response), MSG_TRUNC));
   if (rc == 0) {
diff --git a/include/ziparchive/zip_writer.h b/include/ziparchive/zip_writer.h
index 41ca2e1..08ead48 100644
--- a/include/ziparchive/zip_writer.h
+++ b/include/ziparchive/zip_writer.h
@@ -75,7 +75,8 @@
     uint32_t uncompressed_size;
     uint16_t last_mod_time;
     uint16_t last_mod_date;
-    uint32_t local_file_header_offset;
+    uint32_t padding_length;
+    off64_t local_file_header_offset;
   };
 
   static const char* ErrorCodeString(int32_t error_code);
@@ -172,6 +173,7 @@
   };
 
   FILE* file_;
+  bool seekable_;
   off64_t current_offset_;
   State state_;
   std::vector<FileEntry> files_;
diff --git a/init/Android.mk b/init/Android.mk
index 1ca88d7..730ffc4 100644
--- a/init/Android.mk
+++ b/init/Android.mk
@@ -62,6 +62,7 @@
     action.cpp \
     capabilities.cpp \
     descriptors.cpp \
+    devices.cpp \
     import_parser.cpp \
     init_parser.cpp \
     log.cpp \
@@ -81,7 +82,6 @@
 LOCAL_SRC_FILES:= \
     bootchart.cpp \
     builtins.cpp \
-    devices.cpp \
     init.cpp \
     keychords.cpp \
     property_service.cpp \
@@ -138,6 +138,7 @@
 include $(CLEAR_VARS)
 LOCAL_MODULE := init_tests
 LOCAL_SRC_FILES := \
+    devices_test.cpp \
     init_parser_test.cpp \
     property_service_test.cpp \
     util_test.cpp \
diff --git a/init/devices.cpp b/init/devices.cpp
index 405f92e..6b7bab9 100644
--- a/init/devices.cpp
+++ b/init/devices.cpp
@@ -286,8 +286,7 @@
     }
 }
 
-static void add_platform_device(const char *path)
-{
+void add_platform_device(const char* path) {
     int path_len = strlen(path);
     struct platform_node *bus;
     const char *name = path;
@@ -329,8 +328,7 @@
     return NULL;
 }
 
-static void remove_platform_device(const char *path)
-{
+void remove_platform_device(const char* path) {
     struct listnode *node;
     struct platform_node *bus;
 
@@ -473,8 +471,7 @@
     }
 }
 
-static char **get_character_device_symlinks(struct uevent *uevent)
-{
+char** get_character_device_symlinks(struct uevent* uevent) {
     const char *parent;
     const char *slash;
     char **links;
@@ -526,8 +523,24 @@
     return NULL;
 }
 
-static char **get_block_device_symlinks(struct uevent *uevent)
-{
+// replaces any unacceptable characters with '_', the
+// length of the resulting string is equal to the input string
+void sanitize_partition_name(char* s) {
+    const char* accept =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+
+    if (!s) return;
+
+    while (*s) {
+        s += strspn(s, accept);
+        if (*s) *s++ = '_';
+    }
+}
+
+char** get_block_device_symlinks(struct uevent* uevent) {
     const char *device;
     struct platform_node *pdev;
     const char *slash;
@@ -562,7 +575,7 @@
 
     if (uevent->partition_name) {
         p = strdup(uevent->partition_name);
-        sanitize(p);
+        sanitize_partition_name(p);
         if (strcmp(uevent->partition_name, p)) {
             LOG(VERBOSE) << "Linking partition '" << uevent->partition_name << "' as '" << p << "'";
         }
diff --git a/init/devices.h b/init/devices.h
index 26a064b..1654af7 100644
--- a/init/devices.h
+++ b/init/devices.h
@@ -55,4 +55,11 @@
                          unsigned short wildcard);
 int get_device_fd();
 
-#endif	/* _INIT_DEVICES_H */
+// Exposed for testing
+void add_platform_device(const char* path);
+void remove_platform_device(const char* path);
+char** get_character_device_symlinks(uevent* uevent);
+char** get_block_device_symlinks(struct uevent* uevent);
+void sanitize_partition_name(char* s);
+
+#endif /* _INIT_DEVICES_H */
diff --git a/init/devices_test.cpp b/init/devices_test.cpp
new file mode 100644
index 0000000..f79c96d
--- /dev/null
+++ b/init/devices_test.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2017 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 "devices.h"
+
+#include <string>
+#include <vector>
+
+#include <android-base/scopeguard.h>
+#include <gtest/gtest.h>
+
+template <char** (*Function)(uevent*)>
+void test_get_symlinks(const std::string& platform_device_name, uevent* uevent,
+                       const std::vector<std::string> expected_links) {
+    add_platform_device(platform_device_name.c_str());
+    auto platform_device_remover = android::base::make_scope_guard(
+        [&platform_device_name]() { remove_platform_device(platform_device_name.c_str()); });
+
+    char** result = Function(uevent);
+    auto result_freer = android::base::make_scope_guard([result]() {
+        if (result) {
+            for (int i = 0; result[i]; i++) {
+                free(result[i]);
+            }
+            free(result);
+        }
+    });
+
+    auto expected_size = expected_links.size();
+    if (expected_size == 0) {
+        ASSERT_EQ(nullptr, result);
+    } else {
+        ASSERT_NE(nullptr, result);
+        // First assert size is equal, so we don't overrun expected_links
+        unsigned int size = 0;
+        while (result[size]) ++size;
+        ASSERT_EQ(expected_size, size);
+
+        for (unsigned int i = 0; i < size; ++i) {
+            EXPECT_EQ(expected_links[i], result[i]);
+        }
+    }
+}
+
+TEST(devices, get_character_device_symlinks_success) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name/tty2-1:1.0",
+        .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result{"/dev/usb/ttyname"};
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_pdev_match) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/device/name/tty2-1:1.0", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_nothing_after_platform_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_found) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/bad/bad/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_roothub) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_usb_device) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_slash) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device/name", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_character_device_symlinks_no_final_name) {
+    const char* platform_device = "/devices/platform/some_device_name";
+    uevent uevent = {
+        .path = "/devices/platform/some_device_name/usb/usb_device//", .subsystem = "tty",
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_character_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition) {
+    // These are actual paths from bullhead
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_num) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = nullptr,
+        .partition_num = 1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-num/p1",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_platform_with_partition_only_name) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/f9824900.sdhci/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = "modem",
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{
+        "/dev/block/platform/soc.0/f9824900.sdhci/by-name/modem",
+        "/dev/block/platform/soc.0/f9824900.sdhci/mmcblk0p1",
+    };
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_pci) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/pci0000:00/0000:00:1f.2/mmcblk0",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/pci/pci0000:00/0000:00:1f.2/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_success_vbd) {
+    const char* platform_device = "/devices/do/not/match";
+    uevent uevent = {
+        .path = "/devices/vbd-1234/mmcblk0", .partition_name = nullptr, .partition_num = -1,
+    };
+    std::vector<std::string> expected_result{"/dev/block/vbd/1234/mmcblk0"};
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, get_block_device_symlinks_no_matches) {
+    const char* platform_device = "/devices/soc.0/f9824900.sdhci";
+    uevent uevent = {
+        .path = "/devices/soc.0/not_the_device/mmc_host/mmc0/mmc0:0001/block/mmcblk0p1",
+        .partition_name = nullptr,
+        .partition_num = -1,
+    };
+    std::vector<std::string> expected_result;
+
+    test_get_symlinks<get_block_device_symlinks>(platform_device, &uevent, expected_result);
+}
+
+TEST(devices, sanitize_null) {
+    sanitize_partition_name(nullptr);
+}
+
+TEST(devices, sanitize_empty) {
+    std::string empty;
+    sanitize_partition_name(&empty[0]);
+    EXPECT_EQ(0u, empty.size());
+}
+
+TEST(devices, sanitize_allgood) {
+    std::string good =
+        "abcdefghijklmnopqrstuvwxyz"
+        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+        "0123456789"
+        "_-.";
+    std::string good_copy = good;
+    sanitize_partition_name(&good[0]);
+    EXPECT_EQ(good_copy, good);
+}
+
+TEST(devices, sanitize_somebad) {
+    std::string string = "abc!@#$%^&*()";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("abc__________", string);
+}
+
+TEST(devices, sanitize_allbad) {
+    std::string string = "!@#$%^&*()";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("__________", string);
+}
+
+TEST(devices, sanitize_onebad) {
+    std::string string = ")";
+    sanitize_partition_name(&string[0]);
+    EXPECT_EQ("_", string);
+}
diff --git a/init/util.cpp b/init/util.cpp
index bf4109c..c1b7898 100644
--- a/init/util.cpp
+++ b/init/util.cpp
@@ -235,27 +235,6 @@
     return 0;
 }
 
-/*
- * replaces any unacceptable characters with '_', the
- * length of the resulting string is equal to the input string
- */
-void sanitize(char *s)
-{
-    const char* accept =
-            "abcdefghijklmnopqrstuvwxyz"
-            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-            "0123456789"
-            "_-.";
-
-    if (!s)
-        return;
-
-    while (*s) {
-        s += strspn(s, accept);
-        if (*s) *s++ = '_';
-    }
-}
-
 int wait_for_file(const char* filename, std::chrono::nanoseconds timeout) {
     boot_clock::time_point timeout_time = boot_clock::now() + timeout;
     while (boot_clock::now() < timeout_time) {
diff --git a/init/util.h b/init/util.h
index 1034c9b..38a7bdb 100644
--- a/init/util.h
+++ b/init/util.h
@@ -61,7 +61,6 @@
 unsigned int decode_uid(const char *s);
 
 int mkdir_recursive(const char *pathname, mode_t mode);
-void sanitize(char *p);
 int wait_for_file(const char *filename, std::chrono::nanoseconds timeout);
 void import_kernel_cmdline(bool in_qemu,
                            const std::function<void(const std::string&, const std::string&, bool)>&);
diff --git a/libziparchive/zip_writer.cc b/libziparchive/zip_writer.cc
index 7600528..2edf224 100644
--- a/libziparchive/zip_writer.cc
+++ b/libziparchive/zip_writer.cc
@@ -18,6 +18,7 @@
 
 #include <cstdio>
 #include <sys/param.h>
+#include <sys/stat.h>
 #include <zlib.h>
 #define DEF_MEM_LEVEL 8                // normally in zutil.h?
 
@@ -84,11 +85,19 @@
   delete stream;
 }
 
-ZipWriter::ZipWriter(FILE* f) : file_(f), current_offset_(0), state_(State::kWritingZip),
-                                z_stream_(nullptr, DeleteZStream), buffer_(kBufSize) {
+ZipWriter::ZipWriter(FILE* f) : file_(f), seekable_(false), current_offset_(0),
+                                state_(State::kWritingZip), z_stream_(nullptr, DeleteZStream),
+                                buffer_(kBufSize) {
+  // Check if the file is seekable (regular file). If fstat fails, that's fine, subsequent calls
+  // will fail as well.
+  struct stat file_stats;
+  if (fstat(fileno(f), &file_stats) == 0) {
+    seekable_ = S_ISREG(file_stats.st_mode);
+  }
 }
 
 ZipWriter::ZipWriter(ZipWriter&& writer) : file_(writer.file_),
+                                           seekable_(writer.seekable_),
                                            current_offset_(writer.current_offset_),
                                            state_(writer.state_),
                                            files_(std::move(writer.files_)),
@@ -100,6 +109,7 @@
 
 ZipWriter& ZipWriter::operator=(ZipWriter&& writer) {
   file_ = writer.file_;
+  seekable_ = writer.seekable_;
   current_offset_ = writer.current_offset_;
   state_ = writer.state_;
   files_ = std::move(writer.files_);
@@ -159,6 +169,30 @@
   *out_time = ptm->tm_hour << 11 | ptm->tm_min << 5 | ptm->tm_sec >> 1;
 }
 
+static void CopyFromFileEntry(const ZipWriter::FileEntry& src, bool use_data_descriptor,
+                              LocalFileHeader* dst) {
+  dst->lfh_signature = LocalFileHeader::kSignature;
+  if (use_data_descriptor) {
+    // Set this flag to denote that a DataDescriptor struct will appear after the data,
+    // containing the crc and size fields.
+    dst->gpb_flags |= kGPBDDFlagMask;
+
+    // The size and crc fields must be 0.
+    dst->compressed_size = 0u;
+    dst->uncompressed_size = 0u;
+    dst->crc32 = 0u;
+  } else {
+    dst->compressed_size = src.compressed_size;
+    dst->uncompressed_size = src.uncompressed_size;
+    dst->crc32 = src.crc32;
+  }
+  dst->compression_method = src.compression_method;
+  dst->last_mod_time = src.last_mod_time;
+  dst->last_mod_date = src.last_mod_date;
+  dst->file_name_length = src.path.size();
+  dst->extra_field_length = src.padding_length;
+}
+
 int32_t ZipWriter::StartAlignedEntryWithTime(const char* path, size_t flags,
                                              time_t time, uint32_t alignment) {
   if (state_ != State::kWritingZip) {
@@ -173,66 +207,58 @@
     return kInvalidAlignment;
   }
 
-  current_file_entry_ = {};
-  current_file_entry_.path = path;
-  current_file_entry_.local_file_header_offset = current_offset_;
+  FileEntry file_entry = {};
+  file_entry.local_file_header_offset = current_offset_;
+  file_entry.path = path;
 
-  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(current_file_entry_.path.data()),
-                        current_file_entry_.path.size())) {
+  if (!IsValidEntryName(reinterpret_cast<const uint8_t*>(file_entry.path.data()),
+                        file_entry.path.size())) {
     return kInvalidEntryName;
   }
 
-  LocalFileHeader header = {};
-  header.lfh_signature = LocalFileHeader::kSignature;
-
-  // Set this flag to denote that a DataDescriptor struct will appear after the data,
-  // containing the crc and size fields.
-  header.gpb_flags |= kGPBDDFlagMask;
-
   if (flags & ZipWriter::kCompress) {
-    current_file_entry_.compression_method = kCompressDeflated;
+    file_entry.compression_method = kCompressDeflated;
 
     int32_t result = PrepareDeflate();
     if (result != kNoError) {
       return result;
     }
   } else {
-    current_file_entry_.compression_method = kCompressStored;
+    file_entry.compression_method = kCompressStored;
   }
-  header.compression_method = current_file_entry_.compression_method;
 
-  ExtractTimeAndDate(time, &current_file_entry_.last_mod_time, &current_file_entry_.last_mod_date);
-  header.last_mod_time = current_file_entry_.last_mod_time;
-  header.last_mod_date = current_file_entry_.last_mod_date;
+  ExtractTimeAndDate(time, &file_entry.last_mod_time, &file_entry.last_mod_date);
 
-  header.file_name_length = current_file_entry_.path.size();
-
-  off64_t offset = current_offset_ + sizeof(header) + current_file_entry_.path.size();
+  off_t offset = current_offset_ + sizeof(LocalFileHeader) + file_entry.path.size();
   std::vector<char> zero_padding;
   if (alignment != 0 && (offset & (alignment - 1))) {
     // Pad the extra field so the data will be aligned.
     uint16_t padding = alignment - (offset % alignment);
-    header.extra_field_length = padding;
+    file_entry.padding_length = padding;
     offset += padding;
-    zero_padding.resize(padding);
-    memset(zero_padding.data(), 0, zero_padding.size());
+    zero_padding.resize(padding, 0);
   }
 
+  LocalFileHeader header = {};
+  // Always start expecting a data descriptor. When the data has finished being written,
+  // if it is possible to seek back, the GPB flag will reset and the sizes written.
+  CopyFromFileEntry(file_entry, true /*use_data_descriptor*/, &header);
+
   if (fwrite(&header, sizeof(header), 1, file_) != 1) {
     return HandleError(kIoError);
   }
 
-  if (fwrite(path, sizeof(*path), current_file_entry_.path.size(), file_)
-      != current_file_entry_.path.size()) {
+  if (fwrite(path, sizeof(*path), file_entry.path.size(), file_) != file_entry.path.size()) {
     return HandleError(kIoError);
   }
 
-  if (header.extra_field_length != 0 &&
-      fwrite(zero_padding.data(), 1, header.extra_field_length, file_)
-      != header.extra_field_length) {
+  if (file_entry.padding_length != 0 &&
+      fwrite(zero_padding.data(), 1, file_entry.padding_length, file_)
+      != file_entry.padding_length) {
     return HandleError(kIoError);
   }
 
+  current_file_entry_ = std::move(file_entry);
   current_offset_ = offset;
   state_ = State::kWritingEntry;
   return kNoError;
@@ -405,23 +431,41 @@
     }
   }
 
-  const uint32_t sig = DataDescriptor::kOptSignature;
-  if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
-    state_ = State::kError;
-    return kIoError;
-  }
+  if ((current_file_entry_.compression_method & kCompressDeflated) || !seekable_) {
+    // Some versions of ZIP don't allow STORED data to have a trailing DataDescriptor.
+    // If this file is not seekable, or if the data is compressed, write a DataDescriptor.
+    const uint32_t sig = DataDescriptor::kOptSignature;
+    if (fwrite(&sig, sizeof(sig), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
 
-  DataDescriptor dd = {};
-  dd.crc32 = current_file_entry_.crc32;
-  dd.compressed_size = current_file_entry_.compressed_size;
-  dd.uncompressed_size = current_file_entry_.uncompressed_size;
-  if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
-    return HandleError(kIoError);
+    DataDescriptor dd = {};
+    dd.crc32 = current_file_entry_.crc32;
+    dd.compressed_size = current_file_entry_.compressed_size;
+    dd.uncompressed_size = current_file_entry_.uncompressed_size;
+    if (fwrite(&dd, sizeof(dd), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+    current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
+  } else {
+    // Seek back to the header and rewrite to include the size.
+    if (fseeko(file_, current_file_entry_.local_file_header_offset, SEEK_SET) != 0) {
+      return HandleError(kIoError);
+    }
+
+    LocalFileHeader header = {};
+    CopyFromFileEntry(current_file_entry_, false /*use_data_descriptor*/, &header);
+
+    if (fwrite(&header, sizeof(header), 1, file_) != 1) {
+      return HandleError(kIoError);
+    }
+
+    if (fseeko(file_, current_offset_, SEEK_SET) != 0) {
+      return HandleError(kIoError);
+    }
   }
 
   files_.emplace_back(std::move(current_file_entry_));
-
-  current_offset_ += sizeof(DataDescriptor::kOptSignature) + sizeof(dd);
   state_ = State::kWritingZip;
   return kNoError;
 }
@@ -431,7 +475,7 @@
     return kInvalidState;
   }
 
-  off64_t startOfCdr = current_offset_;
+  off_t startOfCdr = current_offset_;
   for (FileEntry& file : files_) {
     CentralDirectoryRecord cdr = {};
     cdr.record_signature = CentralDirectoryRecord::kSignature;
@@ -443,7 +487,7 @@
     cdr.compressed_size = file.compressed_size;
     cdr.uncompressed_size = file.uncompressed_size;
     cdr.file_name_length = file.path.size();
-    cdr.local_file_header_offset = file.local_file_header_offset;
+    cdr.local_file_header_offset = static_cast<uint32_t>(file.local_file_header_offset);
     if (fwrite(&cdr, sizeof(cdr), 1, file_) != 1) {
       return HandleError(kIoError);
     }
@@ -473,7 +517,7 @@
   // Since we can BackUp() and potentially finish writing at an offset less than one we had
   // already written at, we must truncate the file.
 
-  if (ftruncate64(fileno(file_), current_offset_) != 0) {
+  if (ftruncate(fileno(file_), current_offset_) != 0) {
     return HandleError(kIoError);
   }
 
diff --git a/libziparchive/zip_writer_test.cc b/libziparchive/zip_writer_test.cc
index 30f4950..5b526a4 100644
--- a/libziparchive/zip_writer_test.cc
+++ b/libziparchive/zip_writer_test.cc
@@ -64,6 +64,7 @@
   ZipEntry data;
   ASSERT_EQ(0, FindEntry(handle, ZipString("file.txt"), &data));
   EXPECT_EQ(kCompressStored, data.method);
+  EXPECT_EQ(0u, data.has_data_descriptor);
   EXPECT_EQ(strlen(expected), data.compressed_length);
   ASSERT_EQ(strlen(expected), data.uncompressed_length);
   ASSERT_TRUE(AssertFileEntryContentsEq(expected, handle, &data));
diff --git a/logd/tests/logd_test.cpp b/logd/tests/logd_test.cpp
index d0101ed..88cb67a 100644
--- a/logd/tests/logd_test.cpp
+++ b/logd/tests/logd_test.cpp
@@ -999,16 +999,18 @@
     }
 
     // We may have DAC, but let's not have MAC
-    if (setcon("u:object_r:shell:s0") < 0) {
+    if ((setcon("u:object_r:shell:s0") < 0) && (setcon("u:r:shell:s0") < 0)) {
         int save_errno = errno;
         security_context_t context;
         getcon(&context);
-        fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n", context,
-                strerror(save_errno));
-        freecon(context);
-        _exit(-1);
-        // NOTREACHED
-        return 0;
+        if (strcmp(context, "u:r:shell:s0")) {
+            fprintf(stderr, "setcon(\"u:r:shell:s0\") failed @\"%s\" %s\n",
+                    context, strerror(save_errno));
+            freecon(context);
+            _exit(-1);
+            // NOTREACHED
+            return 0;
+        }
     }
 
     // The key here is we are root, but we are in u:r:shell:s0,