installer: Fix the /mnt/stateful_partition/unencrypted permission.

This patch introduces a postinstall fix (read as hack) to change the
permission of /mnt/stateful_partition/unencrypted (if it exists) to
0755 owned by root:root. This solves some issues with devices coming
out of the factory with these permission wrong, not being able to
read the files pre-populated there.

The patch introces three syscall calls: stat, chmod and chown, that
only log a message to stdout or stderr when they succeed or fail
respectively. All errors are non-fatal.

BUG=chromium:432774
TEST=Manual local test.

Procedure executed in the chroot after commenting out some code that
requires cgpt working on a real device (just to get to run this patch).
Setup:
 $ emerge-link chromeos-install
 $ mkdir /tmp/foo3
 $ cp /build/link/usr/bin/cros_installer /tmp/foo3/
 $ mkdir -p /tmp/foo3/proc && echo cros_legacy > /tmp/foo3/proc/cmdline
 $ mkdir -p /tmp/foo3/etc
 $ echo "CHROMEOS_RELEASE_VERSION=1.2.3.4" > /tmp/foo3/etc/lsb-release
 $ touch /tmp/foo3/foo2 && touch /tmp/foo3/foo3
 $ cp /build/link/usr/bin/cros_installer /tmp/foo3/

Tests the four cases:
 $ sudo chroot /tmp/foo3 /cros_installer postinst / /foo3
...
Couldn't check the current permission, ignored: No such file or directory
...

 $ sudo mkdir -p /tmp/foo3//mnt/stateful_partition/unencrypted/foo
 $ sudo chroot /tmp/foo3 /cros_installer postinst / /foo3
...
Checking /mnt/stateful_partition/unencrypted permission.
Permission is ok.
...

 $ sudo chown 1234:5678 /tmp/foo3/mnt/stateful_partition/unencrypted
 $ sudo chroot /tmp/foo3 /cros_installer postinst / /foo3
...
Checking /mnt/stateful_partition/unencrypted permission.
Permission changed successfully.
...

 $ ls -la /tmp/foo3/mnt/stateful_partition/unencrypted
drwxr-xr-x 3 root root 4096 Nov 14 23:30 .
...

 $ sudo chmod 766 /tmp/foo3/mnt/stateful_partition/unencrypted
 $ sudo chroot /tmp/foo3 /cros_installer postinst / /foo3
...
Checking /mnt/stateful_partition/unencrypted permission.
Permission changed successfully.
...

 $ ls -la /tmp/foo3/mnt/stateful_partition/unencrypted
drwxr-xr-x 3 root root 4096 Nov 14 23:30 .
...

Change-Id: Ib8a3d70e281c1abc5ae80b72f60bdc7f6bc3d2d8
Previous-Reviewed-on: https://chromium-review.googlesource.com/229983
(cherry picked from commit 21489894e6983d197f19b711d918980fa0632238)
Previous-Reviewed-on: https://chromium-review.googlesource.com/230764
(cherry picked from commit 721bc2a15d64f3a2b6175a2768f811e7928b4483)
Reviewed-on: https://chromium-review.googlesource.com/232951
Reviewed-by: Don Garrett <dgarrett@chromium.org>
Tested-by: Alex Deymo <deymo@chromium.org>
diff --git a/installer/chromeos_postinst.cc b/installer/chromeos_postinst.cc
index 6fccfd9..eb9c9c3 100644
--- a/installer/chromeos_postinst.cc
+++ b/installer/chromeos_postinst.cc
@@ -6,6 +6,8 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/types.h>
 #include <unistd.h>
 
 #include "installer/cgpt_manager.h"
@@ -16,6 +18,10 @@
 
 using std::string;
 
+namespace {
+const char kStatefulMount[] = "/mnt/stateful_partition";
+}  // namespace
+
 bool ConfigureInstall(const string& install_dev,
                       const string& install_path,
                       BiosType bios_type,
@@ -145,6 +151,37 @@
   return result;
 }
 
+// Fix the unencrypted permission. The permission on this file have been
+// deployed with wrong values (0766 for the permission) and/or the wrong
+// uid:gid.
+void FixUnencryptedPermission() {
+  string unencrypted_dir = string(kStatefulMount) + "/unencrypted";
+  printf("Checking %s permission.\n", unencrypted_dir.c_str());
+  struct stat unencrypted_stat;
+  const mode_t target_mode = S_IFDIR | S_IRWXU | (S_IRGRP | S_IXGRP) | (
+      S_IROTH | S_IXOTH);  // 040755
+  if (stat(unencrypted_dir.c_str(), &unencrypted_stat) != 0) {
+    perror("Couldn't check the current permission, ignored");
+  } else if (unencrypted_stat.st_uid == 0 &&
+             unencrypted_stat.st_gid == 0 &&
+             unencrypted_stat.st_mode == target_mode) {
+    printf("Permission is ok.\n");
+  } else {
+    bool ok = true;
+    // chmod(2) only takes the last four octal digits, so we flip the IFDIR bit.
+    if (chmod(unencrypted_dir.c_str(), target_mode ^ S_IFDIR) != 0) {
+      perror("chmod");
+      ok = false;
+    }
+    if (chown(unencrypted_dir.c_str(), 0, 0) != 0) {
+      perror("chown");
+      ok = false;
+    }
+    if (ok)
+      printf("Permission changed successfully.\n");
+  }
+}
+
 // Do post install stuff.
 //
 // Install kernel, set up the proper bootable partition in
@@ -252,6 +289,7 @@
   // and a reboot will boot into it. Thus, it's important that any future
   // errors in this script do not cause this script to return failure unless
   // in factory mode.
+  FixUnencryptedPermission();
 
   // We have a new image, making the ureadahead pack files
   // out-of-date.  Delete the files so that ureadahead will
@@ -266,8 +304,7 @@
   // Create a file indicating that the install is completed. The file
   // will be used in /sbin/chromeos_startup to run tasks on the next boot.
   // See comments above about removing ureadahead files.
-  string stateful_mnt = "/mnt/stateful_partition";
-  string install_completed = stateful_mnt + "/.install_completed";
+  string install_completed = string(kStatefulMount) + "/.install_completed";
   if (!Touch(install_completed)) {
     printf("Touch(%s) FAILED\n", install_completed.c_str());
     if (is_factory_install)
@@ -276,7 +313,7 @@
 
   // If present, remove firmware checking completion file to force a disk
   // firmware check at reboot.
-  string disk_fw_check_complete = stateful_mnt +
+  string disk_fw_check_complete = string(kStatefulMount) +
       "/unencrypted/cache/.disk_firmware_upgrade_completed";
   unlink(disk_fw_check_complete.c_str());
 
@@ -372,7 +409,7 @@
   }
 
   // If we can read in the stateful lsb-release we are updating FROM, log it.
-  if (ReadFileToString("/mnt/stateful_partition/etc/lsb-release",
+  if (ReadFileToString(string(kStatefulMount) + "/etc/lsb-release",
                        &lsb_contents)) {
     printf("\nFROM (stateful):\n%s", lsb_contents.c_str());
   }