Support initializing logging with a file descriptor rather than a path.

This CL adds an optional file descriptor to LoggingSettings, which can
be used to initialize logging to an already open file. Previously,
logging to a file required passing a path and letting the logging
system open() it for append. Passing a file-descriptor means that a
process can be sandboxed to have no access to the log directory, but
can still be passed a file-descriptor to log to.

This is needed for the Network Service on ChromeOS, whose logging needs
to be reinitialized to a new location after it's already started.

Bug: 977415
Change-Id: I2d07dc13a7e9f0f57d0802e4816628c181f183cd
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1699477
Commit-Queue: Robbie McElrath <[email protected]>
Reviewed-by: John Abd-El-Malek <[email protected]>
Reviewed-by: Achuith Bhandarkar <[email protected]>
Reviewed-by: Will Harris <[email protected]>
Reviewed-by: Wez <[email protected]>
Cr-Commit-Position: refs/heads/master@{#688754}
diff --git a/ash/shell/content/shell_with_content_main.cc b/ash/shell/content/shell_with_content_main.cc
index b1a6b73a..b959086 100644
--- a/ash/shell/content/shell_with_content_main.cc
+++ b/ash/shell/content/shell_with_content_main.cc
@@ -18,7 +18,7 @@
   log_filename = log_filename.AppendASCII("ash_shell.log");
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file = log_filename.value().c_str();
+  settings.log_file_path = log_filename.value().c_str();
   settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   logging::InitLogging(settings);
   logging::SetLogItems(true /* process_id */, true /* thread_id */,
diff --git a/base/logging.cc b/base/logging.cc
index 2f5c897..04aabb6 100644
--- a/base/logging.cc
+++ b/base/logging.cc
@@ -119,6 +119,10 @@
 #include "base/posix/safe_strerror.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "base/files/scoped_file.h"
+#endif
+
 namespace logging {
 
 namespace {
@@ -313,52 +317,52 @@
     g_log_file_name = new PathString(GetDefaultLogFile());
   }
 
-  if ((g_logging_destination & LOG_TO_FILE) != 0) {
+  if ((g_logging_destination & LOG_TO_FILE) == 0)
+    return true;
+
 #if defined(OS_WIN)
-    // The FILE_APPEND_DATA access mask ensures that the file is atomically
-    // appended to across accesses from multiple threads.
-    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx
-    // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
+  // The FILE_APPEND_DATA access mask ensures that the file is atomically
+  // appended to across accesses from multiple threads.
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa364399(v=vs.85).aspx
+  // https://msdn.microsoft.com/en-us/library/windows/desktop/aa363858(v=vs.85).aspx
+  g_log_file = CreateFile(base::as_wcstr(*g_log_file_name), FILE_APPEND_DATA,
+                          FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
+                          OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
+  if (g_log_file == INVALID_HANDLE_VALUE || g_log_file == nullptr) {
+    // We are intentionally not using FilePath or FileUtil here to reduce the
+    // dependencies of the logging implementation. For e.g. FilePath and
+    // FileUtil depend on shell32 and user32.dll. This is not acceptable for
+    // some consumers of base logging like chrome_elf, etc.
+    // Please don't change the code below to use FilePath.
+    // try the current directory
+    base::char16 system_buffer[MAX_PATH];
+    system_buffer[0] = 0;
+    DWORD len = ::GetCurrentDirectory(base::size(system_buffer),
+                                      base::as_writable_wcstr(system_buffer));
+    if (len == 0 || len > base::size(system_buffer))
+      return false;
+
+    *g_log_file_name = system_buffer;
+    // Append a trailing backslash if needed.
+    if (g_log_file_name->back() != L'\\')
+      *g_log_file_name += STRING16_LITERAL("\\");
+    *g_log_file_name += STRING16_LITERAL("debug.log");
+
     g_log_file = CreateFile(base::as_wcstr(*g_log_file_name), FILE_APPEND_DATA,
                             FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
                             OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
     if (g_log_file == INVALID_HANDLE_VALUE || g_log_file == nullptr) {
-      // We are intentionally not using FilePath or FileUtil here to reduce the
-      // dependencies of the logging implementation. For e.g. FilePath and
-      // FileUtil depend on shell32 and user32.dll. This is not acceptable for
-      // some consumers of base logging like chrome_elf, etc.
-      // Please don't change the code below to use FilePath.
-      // try the current directory
-      base::char16 system_buffer[MAX_PATH];
-      system_buffer[0] = 0;
-      DWORD len = ::GetCurrentDirectory(base::size(system_buffer),
-                                        base::as_writable_wcstr(system_buffer));
-      if (len == 0 || len > base::size(system_buffer))
-        return false;
-
-      *g_log_file_name = system_buffer;
-      // Append a trailing backslash if needed.
-      if (g_log_file_name->back() != L'\\')
-        *g_log_file_name += STRING16_LITERAL("\\");
-      *g_log_file_name += STRING16_LITERAL("debug.log");
-
-      g_log_file =
-          CreateFile(base::as_wcstr(*g_log_file_name), FILE_APPEND_DATA,
-                     FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS,
-                     FILE_ATTRIBUTE_NORMAL, nullptr);
-      if (g_log_file == INVALID_HANDLE_VALUE || g_log_file == nullptr) {
-        g_log_file = nullptr;
-        return false;
-      }
-    }
-#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
-    g_log_file = fopen(g_log_file_name->c_str(), "a");
-    if (g_log_file == nullptr)
+      g_log_file = nullptr;
       return false;
+    }
+  }
+#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
+  g_log_file = fopen(g_log_file_name->c_str(), "a");
+  if (g_log_file == nullptr)
+    return false;
 #else
 #error Unsupported platform
 #endif
-  }
 
   return true;
 }
@@ -379,6 +383,11 @@
 
   CloseFile(g_log_file);
   g_log_file = nullptr;
+
+  // If we initialized logging via an externally-provided file descriptor, we
+  // won't have a log path set and shouldn't try to reopen the log file.
+  if (!g_log_file_name)
+    g_logging_destination &= ~LOG_TO_FILE;
 }
 
 }  // namespace
@@ -401,6 +410,7 @@
   CHECK_EQ(settings.logging_dest & ~(LOG_TO_SYSTEM_DEBUG_LOG | LOG_TO_STDERR),
            0u);
 #endif
+
   base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
   // Don't bother initializing |g_vlog_info| unless we use one of the
   // vlog switches.
@@ -439,7 +449,7 @@
     return true;
 
 #if defined(OS_POSIX) || defined(OS_FUCHSIA)
-  LoggingLock::Init(settings.lock_log, settings.log_file);
+  LoggingLock::Init(settings.lock_log, settings.log_file_path);
   LoggingLock logging_lock;
 #endif
 
@@ -447,9 +457,17 @@
   // default log file will re-initialize to the new options.
   CloseLogFileUnlocked();
 
+#if defined(OS_CHROMEOS)
+  if (settings.log_file) {
+    DCHECK(!settings.log_file_path);
+    g_log_file = settings.log_file;
+    return true;
+  }
+#endif
+
   if (!g_log_file_name)
     g_log_file_name = new PathString();
-  *g_log_file_name = settings.log_file;
+  *g_log_file_name = settings.log_file_path;
   if (settings.delete_old == DELETE_OLD_LOG_FILE)
     DeleteFilePath(*g_log_file_name);
 
@@ -1078,6 +1096,25 @@
   CloseLogFileUnlocked();
 }
 
+#if defined(OS_CHROMEOS)
+FILE* DuplicateLogFILE() {
+  if ((g_logging_destination & LOG_TO_FILE) == 0 || !InitializeLogFileHandle())
+    return nullptr;
+
+  int log_fd = fileno(g_log_file);
+  if (log_fd == -1)
+    return nullptr;
+  base::ScopedFD dup_fd(dup(log_fd));
+  if (dup_fd == -1)
+    return nullptr;
+  FILE* duplicate = fdopen(dup_fd.get(), "a");
+  if (!duplicate)
+    return nullptr;
+  ignore_result(dup_fd.release());
+  return duplicate;
+}
+#endif
+
 void RawLog(int level, const char* message) {
   if (level >= g_min_log_level && message) {
     size_t bytes_written = 0;
diff --git a/base/logging.h b/base/logging.h
index e3af43a..772c274 100644
--- a/base/logging.h
+++ b/base/logging.h
@@ -26,6 +26,10 @@
 #include "base/template_util.h"
 #include "build/build_config.h"
 
+#if defined(OS_CHROMEOS)
+#include <cstdio>
+#endif
+
 //
 // Optional message capabilities
 // -----------------------------
@@ -214,11 +218,18 @@
   // destinations.
   uint32_t logging_dest = LOG_DEFAULT;
 
-  // The three settings below have an effect only when LOG_TO_FILE is
+  // The four settings below have an effect only when LOG_TO_FILE is
   // set in |logging_dest|.
-  const PathChar* log_file = nullptr;
+  const PathChar* log_file_path = nullptr;
   LogLockingState lock_log = LOCK_LOG_FILE;
   OldFileDeletionState delete_old = APPEND_TO_OLD_LOG_FILE;
+#if defined(OS_CHROMEOS)
+  // Contains an optional file that logs should be written to. If present,
+  // |log_file_path| will be ignored, and the logging system will take ownership
+  // of the FILE. If there's an error writing to this file, no fallback paths
+  // will be opened.
+  FILE* log_file = nullptr;
+#endif
 };
 
 // Define different names for the BaseInitLoggingImpl() function depending on
@@ -988,6 +999,14 @@
 //       after this call.
 BASE_EXPORT void CloseLogFile();
 
+#if defined(OS_CHROMEOS)
+// Returns a new file handle that will write to the same destination as the
+// currently open log file. Returns nullptr if logging to a file is disabled,
+// or if opening the file failed. This is intended to be used to initialize
+// logging in child processes that are unable to open files.
+BASE_EXPORT FILE* DuplicateLogFILE();
+#endif
+
 // Async signal safe logging mechanism.
 BASE_EXPORT void RawLog(int level, const char* message);
 
diff --git a/base/logging_unittest.cc b/base/logging_unittest.cc
index 6290c47..8de7f30 100644
--- a/base/logging_unittest.cc
+++ b/base/logging_unittest.cc
@@ -260,7 +260,7 @@
   base::FilePath file_logs_path;
   if (log_destinations & LOG_TO_FILE) {
     file_logs_path = temp_dir.GetPath().Append("file.log");
-    settings.log_file = file_logs_path.value().c_str();
+    settings.log_file_path = file_logs_path.value().c_str();
   }
   InitLogging(settings);
 
@@ -316,6 +316,64 @@
 }
 #endif
 
+#if defined(OS_CHROMEOS)
+TEST_F(LoggingTest, InitWithFileDescriptor) {
+  const char kErrorLogMessage[] = "something bad happened";
+
+  // Open a file to pass to the InitLogging.
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_log_path = temp_dir.GetPath().Append("file.log");
+  FILE* log_file = fopen(file_log_path.value().c_str(), "w");
+  CHECK(log_file);
+
+  // Set up logging.
+  LoggingSettings settings;
+  settings.logging_dest = LOG_TO_FILE;
+  settings.log_file = log_file;
+  InitLogging(settings);
+
+  LOG(ERROR) << kErrorLogMessage;
+
+  // Check the message was written to the log file.
+  std::string written_logs;
+  ASSERT_TRUE(base::ReadFileToString(file_log_path, &written_logs));
+  ASSERT_NE(written_logs.find(kErrorLogMessage), std::string::npos);
+}
+
+TEST_F(LoggingTest, DuplicateLogFile) {
+  const char kErrorLogMessage1[] = "something really bad happened";
+  const char kErrorLogMessage2[] = "some other bad thing happened";
+
+  base::ScopedTempDir temp_dir;
+  ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+  base::FilePath file_log_path = temp_dir.GetPath().Append("file.log");
+
+  // Set up logging.
+  LoggingSettings settings;
+  settings.logging_dest = LOG_TO_FILE;
+  settings.log_file_path = file_log_path.value().c_str();
+  InitLogging(settings);
+
+  LOG(ERROR) << kErrorLogMessage1;
+
+  // Duplicate the log FILE, close the original (to make sure we actually
+  // duplicated it), and write to the duplicate.
+  FILE* log_file_dup = DuplicateLogFILE();
+  CHECK(log_file_dup);
+  CloseLogFile();
+  fprintf(log_file_dup, "%s\n", kErrorLogMessage2);
+  fflush(log_file_dup);
+
+  // Check the messages were written to the log file.
+  std::string written_logs;
+  ASSERT_TRUE(base::ReadFileToString(file_log_path, &written_logs));
+  ASSERT_NE(written_logs.find(kErrorLogMessage1), std::string::npos);
+  ASSERT_NE(written_logs.find(kErrorLogMessage2), std::string::npos);
+  fclose(log_file_dup);
+}
+#endif  // defined(OS_CHROMEOS)
+
 // Official builds have CHECKs directly call BreakDebugger.
 #if !defined(OFFICIAL_BUILD)
 
diff --git a/base/test/test_suite.cc b/base/test/test_suite.cc
index 7807d031..0e35d3c 100644
--- a/base/test/test_suite.cc
+++ b/base/test/test_suite.cc
@@ -176,7 +176,7 @@
 #endif  // defined(OS_FUCHSIA)
 
   logging::LoggingSettings settings;
-  settings.log_file = log_filename.value().c_str();
+  settings.log_file_path = log_filename.value().c_str();
   settings.logging_dest = logging::LOG_TO_ALL;
   settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   logging::InitLogging(settings);
diff --git a/chrome/browser/chromeos/logging.cc b/chrome/browser/chromeos/logging.cc
index 7676a7ab..e569391 100644
--- a/chrome/browser/chromeos/logging.cc
+++ b/chrome/browser/chromeos/logging.cc
@@ -4,6 +4,8 @@
 
 #include "chrome/browser/chromeos/logging.h"
 
+#include <cstdio>
+
 #include "base/bind.h"
 #include "base/command_line.h"
 #include "base/files/file_path.h"
@@ -15,6 +17,7 @@
 #include "content/public/browser/browser_thread.h"
 #include "content/public/browser/network_service_instance.h"
 #include "content/public/common/network_service_util.h"
+#include "mojo/public/cpp/system/platform_handle.h"
 #include "services/network/public/mojom/network_service.mojom.h"
 
 namespace logging {
@@ -23,7 +26,7 @@
 
 // This should be true for exactly the period between the end of
 // InitChromeLogging() and the beginning of CleanupChromeLogging().
-bool chrome_logging_redirected_ = false;
+bool g_chrome_logging_redirected = false;
 
 // This should be set to true for tests that rely on log redirection.
 bool g_force_log_redirection = false;
@@ -37,20 +40,26 @@
   // deleted if it already exists.
   logging::LoggingSettings settings;
   settings.logging_dest = DetermineLoggingDestination(command_line);
-  settings.log_file = log_path.value().c_str();
+  settings.log_file_path = log_path.value().c_str();
   if (!logging::InitLogging(settings)) {
     DLOG(ERROR) << "Unable to initialize logging to " << log_path.value();
     base::PostTask(FROM_HERE, {base::ThreadPool(), base::MayBlock()},
                    base::BindOnce(&RemoveSymlinkAndLog, log_path, target_path));
     return;
   }
-  chrome_logging_redirected_ = true;
+  g_chrome_logging_redirected = true;
 
   // Redirect the Network Service's logs as well if it's running out of process.
   if (content::IsOutOfProcessNetworkService()) {
     auto logging_settings = network::mojom::LoggingSettings::New();
     logging_settings->logging_dest = settings.logging_dest;
-    logging_settings->log_file = log_path;
+    const int log_file_descriptor = fileno(logging::DuplicateLogFILE());
+    if (log_file_descriptor < 0) {
+      DLOG(WARNING) << "Unable to duplicate log file handle";
+      return;
+    }
+    logging_settings->log_file_descriptor =
+        mojo::WrapPlatformFile(log_file_descriptor);
     content::GetNetworkService()->ReinitializeLogging(
         std::move(logging_settings));
   }
@@ -70,7 +79,7 @@
   if (!base::SysInfo::IsRunningOnChromeOS() && !g_force_log_redirection)
     return;
 
-  if (chrome_logging_redirected_) {
+  if (g_chrome_logging_redirected) {
     // TODO: Support multiple active users. http://crbug.com/230345
     LOG(WARNING) << "NOT redirecting logging for multi-profiles case.";
     return;
diff --git a/chrome/chrome_cleaner/logging/scoped_logging.cc b/chrome/chrome_cleaner/logging/scoped_logging.cc
index bb40580..64a2df1 100644
--- a/chrome/chrome_cleaner/logging/scoped_logging.cc
+++ b/chrome/chrome_cleaner/logging/scoped_logging.cc
@@ -65,7 +65,7 @@
 
   logging::LoggingSettings logging_settings;
   logging_settings.logging_dest = logging::LOG_TO_FILE;
-  logging_settings.log_file = log_file_path.value().c_str();
+  logging_settings.log_file_path = log_file_path.value().c_str();
 
   bool success = logging::InitLogging(logging_settings);
   DCHECK(success);
diff --git a/chrome/chrome_cleaner/test/test_process_main.cc b/chrome/chrome_cleaner/test/test_process_main.cc
index b6d6a1e..eca0f61 100644
--- a/chrome/chrome_cleaner/test/test_process_main.cc
+++ b/chrome/chrome_cleaner/test/test_process_main.cc
@@ -48,7 +48,7 @@
       exe_file_path.ReplaceExtension(kLogFileExtension));
   logging::LoggingSettings logging_settings;
   logging_settings.logging_dest = logging::LOG_TO_FILE;
-  logging_settings.log_file = log_file_path.value().c_str();
+  logging_settings.log_file_path = log_file_path.value().c_str();
   success = logging::InitLogging(logging_settings);
   DCHECK(success);
 
diff --git a/chrome/chrome_cleaner/test/test_service_main.cc b/chrome/chrome_cleaner/test/test_service_main.cc
index 5abf73b1..68f0b429 100644
--- a/chrome/chrome_cleaner/test/test_service_main.cc
+++ b/chrome/chrome_cleaner/test/test_service_main.cc
@@ -132,7 +132,7 @@
       exe_file_path.ReplaceExtension(kLogFileExtension));
   logging::LoggingSettings logging_settings;
   logging_settings.logging_dest = logging::LOG_TO_FILE;
-  logging_settings.log_file = log_file_path.value().c_str();
+  logging_settings.log_file_path = log_file_path.value().c_str();
   success = logging::InitLogging(logging_settings);
   DCHECK(success);
 
diff --git a/chrome/chrome_watcher/chrome_watcher_main.cc b/chrome/chrome_watcher/chrome_watcher_main.cc
index 246ff17e..1a93c2f7 100644
--- a/chrome/chrome_watcher/chrome_watcher_main.cc
+++ b/chrome/chrome_watcher/chrome_watcher_main.cc
@@ -182,7 +182,7 @@
 
   LoggingSettings settings;
   settings.logging_dest = logging_dest;
-  settings.log_file = log_path.value().c_str();
+  settings.log_file_path = log_path.value().c_str();
   settings.lock_log = log_locking_state;
   settings.delete_old = delete_old_log_file;
   bool success = InitLogging(settings);
diff --git a/chrome/common/logging_chrome.cc b/chrome/common/logging_chrome.cc
index d7a7e0f..56c9d57 100644
--- a/chrome/common/logging_chrome.cc
+++ b/chrome/common/logging_chrome.cc
@@ -301,7 +301,7 @@
 
   LoggingSettings settings;
   settings.logging_dest = logging_dest;
-  settings.log_file = log_path.value().c_str();
+  settings.log_file_path = log_path.value().c_str();
   settings.lock_log = log_locking_state;
   settings.delete_old = delete_old_log_file;
   bool success = InitLogging(settings);
diff --git a/chrome/credential_provider/setup/setup.cc b/chrome/credential_provider/setup/setup.cc
index 3c6d36b..d21b82d 100644
--- a/chrome/credential_provider/setup/setup.cc
+++ b/chrome/credential_provider/setup/setup.cc
@@ -84,7 +84,7 @@
   base::FilePath log_file_path = cmdline->GetSwitchValuePath("log-file");
   if (!log_file_path.empty()) {
     settings.logging_dest = logging::LOG_TO_FILE;
-    settings.log_file = log_file_path.value().c_str();
+    settings.log_file_path = log_file_path.value().c_str();
   }
 
   logging::InitLogging(settings);
diff --git a/chrome/installer/util/logging_installer.cc b/chrome/installer/util/logging_installer.cc
index 9473692..70d3642 100644
--- a/chrome/installer/util/logging_installer.cc
+++ b/chrome/installer/util/logging_installer.cc
@@ -88,7 +88,7 @@
 
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_FILE;
-  settings.log_file = log_file_path.value().c_str();
+  settings.log_file_path = log_file_path.value().c_str();
   logging::InitLogging(settings);
 
   if (prefs.GetBool(installer::master_preferences::kVerboseLogging,
diff --git a/chrome/updater/updater.cc b/chrome/updater/updater.cc
index 27012e5..18fa3c5 100644
--- a/chrome/updater/updater.cc
+++ b/chrome/updater/updater.cc
@@ -104,14 +104,14 @@
   base::FilePath log_dir;
   GetProductDirectory(&log_dir);
   const auto log_file = log_dir.Append(FILE_PATH_LITERAL("updater.log"));
-  settings.log_file = log_file.value().c_str();
+  settings.log_file_path = log_file.value().c_str();
   settings.logging_dest = logging::LOG_TO_ALL;
   logging::InitLogging(settings);
   logging::SetLogItems(true,    // enable_process_id
                        true,    // enable_thread_id
                        true,    // enable_timestamp
                        false);  // enable_tickcount
-  VLOG(1) << "Log file " << settings.log_file;
+  VLOG(1) << "Log file " << settings.log_file_path;
 }
 
 void InitializeUpdaterMain() {
diff --git a/chromecast/app/cast_main_delegate.cc b/chromecast/app/cast_main_delegate.cc
index 7c8554d..3b18f4fe 100644
--- a/chromecast/app/cast_main_delegate.cc
+++ b/chromecast/app/cast_main_delegate.cc
@@ -83,7 +83,7 @@
     base::PathService::Get(FILE_CAST_ANDROID_LOG, &log_file);
     settings.logging_dest =
         logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
-    settings.log_file = log_file.value().c_str();
+    settings.log_file_path = log_file.value().c_str();
     settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   }
 #endif  // defined(OS_ANDROID)
diff --git a/components/zucchini/zucchini_main.cc b/components/zucchini/zucchini_main.cc
index ab2c3e7..9b5e505 100644
--- a/components/zucchini/zucchini_main.cc
+++ b/components/zucchini/zucchini_main.cc
@@ -20,7 +20,7 @@
   logging::LoggingSettings settings;
   settings.logging_dest =
       logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
-  settings.log_file = nullptr;
+  settings.log_file_path = nullptr;
   settings.lock_log = logging::DONT_LOCK_LOG_FILE;
   settings.delete_old = logging::APPEND_TO_OLD_LOG_FILE;
   bool logging_res = logging::InitLogging(settings);
diff --git a/content/shell/app/shell_main_delegate.cc b/content/shell/app/shell_main_delegate.cc
index bee1b4c4..f51ad89 100644
--- a/content/shell/app/shell_main_delegate.cc
+++ b/content/shell/app/shell_main_delegate.cc
@@ -138,7 +138,7 @@
 
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file = log_filename.value().c_str();
+  settings.log_file_path = log_filename.value().c_str();
   settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   logging::InitLogging(settings);
   logging::SetLogItems(true /* Process ID */, true /* Thread ID */,
diff --git a/courgette/courgette_tool.cc b/courgette/courgette_tool.cc
index 0aade2f..383a92dc 100644
--- a/courgette/courgette_tool.cc
+++ b/courgette/courgette_tool.cc
@@ -410,7 +410,7 @@
         logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
   } else {
     settings.logging_dest = logging::LOG_TO_ALL;
-    settings.log_file = FILE_PATH_LITERAL("courgette.log");
+    settings.log_file_path = FILE_PATH_LITERAL("courgette.log");
   }
   (void)logging::InitLogging(settings);
   logging::SetMinLogLevel(logging::LOG_VERBOSE);
diff --git a/extensions/shell/app/shell_main_delegate.cc b/extensions/shell/app/shell_main_delegate.cc
index 929c85e..72de771 100644
--- a/extensions/shell/app/shell_main_delegate.cc
+++ b/extensions/shell/app/shell_main_delegate.cc
@@ -99,7 +99,7 @@
   // Set up log initialization settings.
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file = log_path.value().c_str();
+  settings.log_file_path = log_path.value().c_str();
 
   // Replace the old log file if this is the first process.
   std::string process_type =
diff --git a/fuchsia/engine/web_engine_main_delegate.cc b/fuchsia/engine/web_engine_main_delegate.cc
index 9ef5b497..c796b92 100644
--- a/fuchsia/engine/web_engine_main_delegate.cc
+++ b/fuchsia/engine/web_engine_main_delegate.cc
@@ -31,7 +31,7 @@
   }
   if (command_line.HasSwitch(switches::kLogFile)) {
     settings.logging_dest |= logging::LOG_TO_FILE;
-    settings.log_file =
+    settings.log_file_path =
         command_line.GetSwitchValueASCII(switches::kLogFile).c_str();
     settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   }
diff --git a/headless/lib/headless_content_main_delegate.cc b/headless/lib/headless_content_main_delegate.cc
index 3bbf129..500627b 100644
--- a/headless/lib/headless_content_main_delegate.cc
+++ b/headless/lib/headless_content_main_delegate.cc
@@ -233,7 +233,7 @@
   }
 
   settings.logging_dest = log_mode;
-  settings.log_file = log_path.value().c_str();
+  settings.log_file_path = log_path.value().c_str();
   settings.lock_log = logging::DONT_LOCK_LOG_FILE;
   settings.delete_old = process_type.empty() ? logging::DELETE_OLD_LOG_FILE
                                              : logging::APPEND_TO_OLD_LOG_FILE;
diff --git a/jingle/glue/logging_unittest.cc b/jingle/glue/logging_unittest.cc
index 0ba0858..8c5cce5 100644
--- a/jingle/glue/logging_unittest.cc
+++ b/jingle/glue/logging_unittest.cc
@@ -63,7 +63,7 @@
   // The command line flags are parsed here and the log file name is set.
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_FILE;
-  settings.log_file = log_file_name;
+  settings.log_file_path = log_file_name;
   settings.lock_log = logging::DONT_LOCK_LOG_FILE;
   settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   if (!logging::InitLogging(settings)) {
diff --git a/net/dns/dns_record_fuzzer.cc b/net/dns/dns_record_fuzzer.cc
index f96083a..396dc16 100644
--- a/net/dns/dns_record_fuzzer.cc
+++ b/net/dns/dns_record_fuzzer.cc
@@ -17,7 +17,7 @@
   logging::LoggingSettings settings;
   settings.logging_dest =
       logging::LOG_TO_SYSTEM_DEBUG_LOG | logging::LOG_TO_STDERR;
-  settings.log_file = nullptr;
+  settings.log_file_path = nullptr;
   logging::InitLogging(settings);
 }
 
diff --git a/net/tools/testserver/run_testserver.cc b/net/tools/testserver/run_testserver.cc
index 2f69598..2ee38f1f 100644
--- a/net/tools/testserver/run_testserver.cc
+++ b/net/tools/testserver/run_testserver.cc
@@ -33,7 +33,7 @@
 
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file = FILE_PATH_LITERAL("testserver.log");
+  settings.log_file_path = FILE_PATH_LITERAL("testserver.log");
   if (!logging::InitLogging(settings)) {
     printf("Error: could not initialize logging. Exiting.\n");
     return -1;
diff --git a/net/tools/tld_cleanup/tld_cleanup.cc b/net/tools/tld_cleanup/tld_cleanup.cc
index 970e107..2c33e66a 100644
--- a/net/tools/tld_cleanup/tld_cleanup.cc
+++ b/net/tools/tld_cleanup/tld_cleanup.cc
@@ -60,7 +60,7 @@
   log_filename = log_filename.AppendASCII("tld_cleanup.log");
   logging::LoggingSettings settings;
   settings.logging_dest = destination;
-  settings.log_file = log_filename.value().c_str();
+  settings.log_file_path = log_filename.value().c_str();
   settings.delete_old = logging::DELETE_OLD_LOG_FILE;
   logging::InitLogging(settings);
 
diff --git a/services/network/network_service.cc b/services/network/network_service.cc
index 549fe42..baae4c8 100644
--- a/services/network/network_service.cc
+++ b/services/network/network_service.cc
@@ -69,6 +69,10 @@
 #include "net/android/http_auth_negotiate_android.h"
 #endif
 
+#if defined(OS_CHROMEOS)
+#include "mojo/public/cpp/system/platform_handle.h"
+#endif
+
 namespace network {
 
 namespace {
@@ -363,9 +367,20 @@
 void NetworkService::ReinitializeLogging(mojom::LoggingSettingsPtr settings) {
   logging::LoggingSettings logging_settings;
   logging_settings.logging_dest = settings->logging_dest;
-  logging_settings.log_file = settings->log_file.value().c_str();
+  int log_file_descriptor = -1;
+  if (mojo::UnwrapPlatformFile(std::move(settings->log_file_descriptor),
+                               &log_file_descriptor) != MOJO_RESULT_OK ||
+      log_file_descriptor < 0) {
+    LOG(ERROR) << "Failed to read new log file handle";
+    return;
+  }
+  logging_settings.log_file = fdopen(log_file_descriptor, "a");
+  if (!logging_settings.log_file) {
+    LOG(ERROR) << "Failed to open new log file handle";
+    return;
+  }
   if (!logging::InitLogging(logging_settings))
-    LOG(ERROR) << "Unable to reinitialize logging to " << settings->log_file;
+    LOG(ERROR) << "Unable to reinitialize logging";
 }
 #endif
 
diff --git a/services/network/public/mojom/network_service.mojom b/services/network/public/mojom/network_service.mojom
index b0ae869..4f03c8a 100644
--- a/services/network/public/mojom/network_service.mojom
+++ b/services/network/public/mojom/network_service.mojom
@@ -180,7 +180,7 @@
 [EnableIf=is_chromeos]
 struct LoggingSettings {
   uint32 logging_dest;
-  mojo_base.mojom.FilePath log_file;
+  handle log_file_descriptor;
 };
 
 // Browser interface to the network service.
diff --git a/tools/android/forwarder2/daemon.cc b/tools/android/forwarder2/daemon.cc
index fb5b80b..2d9d0ad3 100644
--- a/tools/android/forwarder2/daemon.cc
+++ b/tools/android/forwarder2/daemon.cc
@@ -44,7 +44,7 @@
   settings.logging_dest = log_file.empty() ? logging::LOG_TO_SYSTEM_DEBUG_LOG |
                                                  logging::LOG_TO_STDERR
                                            : logging::LOG_TO_FILE;
-  settings.log_file = log_file.c_str();
+  settings.log_file_path = log_file.c_str();
   settings.lock_log = logging::DONT_LOCK_LOG_FILE;
   CHECK(logging::InitLogging(settings));
 }
diff --git a/tools/ipc_fuzzer/message_replay/replay_process.cc b/tools/ipc_fuzzer/message_replay/replay_process.cc
index 05ff067..bf56145 100644
--- a/tools/ipc_fuzzer/message_replay/replay_process.cc
+++ b/tools/ipc_fuzzer/message_replay/replay_process.cc
@@ -113,7 +113,7 @@
   logging::SetMinLogLevel(logging::LOG_ERROR);
   logging::LoggingSettings settings;
   settings.logging_dest = logging::LOG_TO_ALL;
-  settings.log_file = FILE_PATH_LITERAL("ipc_replay.log");
+  settings.log_file_path = FILE_PATH_LITERAL("ipc_replay.log");
   logging::InitLogging(settings);
 
   // Make sure to initialize Mojo before starting the IO thread.
diff --git a/ui/ozone/demo/ozone_demo.cc b/ui/ozone/demo/ozone_demo.cc
index 8be648b..5a6a833 100644
--- a/ui/ozone/demo/ozone_demo.cc
+++ b/ui/ozone/demo/ozone_demo.cc
@@ -37,7 +37,7 @@
 
 // Logs to system debug by default on POSIX.
 #if defined(OS_WIN)
-  settings.log_file = FILE_PATH_LITERAL("ozone_demo.log");
+  settings.log_file_path = FILE_PATH_LITERAL("ozone_demo.log");
 #endif
 
   logging::InitLogging(settings);