1b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// Copyright 2013 The Chromium Authors. All rights reserved.
2b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)// found in the LICENSE file.
4b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
5b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/ui/webui/sync_file_system_internals/sync_file_system_internals_handler.h"
6b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <vector>
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
9b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/bind.h"
10b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/bind_helpers.h"
11b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "base/values.h"
127d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/drive/drive_notification_manager.h"
137d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)#include "chrome/browser/drive/drive_notification_manager_factory.h"
14b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/extensions/api/sync_file_system/sync_file_system_api_helpers.h"
15b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/profiles/profile.h"
1690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)#include "chrome/browser/sync_file_system/logger.h"
17b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/sync_file_system/sync_file_system_service.h"
18b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/sync_file_system/sync_file_system_service_factory.h"
19b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/browser/sync_file_system/sync_service_state.h"
20b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "chrome/common/extensions/api/sync_file_system.h"
21b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "content/public/browser/storage_partition.h"
22b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)#include "content/public/browser/web_ui.h"
23a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)#include "google_apis/drive/time_util.h"
24b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
25eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochusing drive::EventLogger;
26b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)using sync_file_system::SyncFileSystemServiceFactory;
27b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)using sync_file_system::SyncServiceState;
28b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
29b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)namespace syncfs_internals {
30b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
31b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)SyncFileSystemInternalsHandler::SyncFileSystemInternalsHandler(Profile* profile)
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    : profile_(profile),
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      observing_task_log_(false) {
345e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  sync_file_system::SyncFileSystemService* sync_service =
355e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)      SyncFileSystemServiceFactory::GetForProfile(profile);
36effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (sync_service)
37effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    sync_service->AddSyncEventObserver(this);
385e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)}
39b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
405e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)SyncFileSystemInternalsHandler::~SyncFileSystemInternalsHandler() {
415e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  sync_file_system::SyncFileSystemService* sync_service =
425e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)      SyncFileSystemServiceFactory::GetForProfile(profile_);
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!sync_service)
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  sync_service->RemoveSyncEventObserver(this);
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (observing_task_log_)
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    sync_service->task_logger()->RemoveObserver(this);
485e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)}
49b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
50b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void SyncFileSystemInternalsHandler::RegisterMessages() {
51b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)  web_ui()->RegisterMessageCallback(
52b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      "getServiceStatus",
53b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      base::Bind(&SyncFileSystemInternalsHandler::GetServiceStatus,
54b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                 base::Unretained(this)));
5590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  web_ui()->RegisterMessageCallback(
5690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      "getLog",
5790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      base::Bind(&SyncFileSystemInternalsHandler::GetLog,
5890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 base::Unretained(this)));
5990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  web_ui()->RegisterMessageCallback(
60a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      "clearLogs",
61a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)      base::Bind(&SyncFileSystemInternalsHandler::ClearLogs,
62a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)                 base::Unretained(this)));
63a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  web_ui()->RegisterMessageCallback(
6490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      "getNotificationSource",
6590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      base::Bind(&SyncFileSystemInternalsHandler::GetNotificationSource,
6690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                 base::Unretained(this)));
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  web_ui()->RegisterMessageCallback(
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      "observeTaskLog",
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      base::Bind(&SyncFileSystemInternalsHandler::ObserveTaskLog,
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                 base::Unretained(this)));
71b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
72b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
735e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)void SyncFileSystemInternalsHandler::OnSyncStateUpdated(
745e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    const GURL& app_origin,
755e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    sync_file_system::SyncServiceState state,
765e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    const std::string& description) {
775e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  std::string state_string = extensions::api::sync_file_system::ToString(
785e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)        extensions::SyncServiceStateToExtensionEnum(state));
795e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  if (!description.empty())
805e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    state_string += " (" + description + ")";
815e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
825e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  // TODO(calvinlo): OnSyncStateUpdated should be updated to also provide the
835e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)  // notification mechanism (XMPP or Polling).
847d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  web_ui()->CallJavascriptFunction("SyncService.onGetServiceStatus",
855e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)                                   base::StringValue(state_string));
865e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)}
875e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
885e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)void SyncFileSystemInternalsHandler::OnFileSynced(
8903b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    const storage::FileSystemURL& url,
905e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    sync_file_system::SyncFileStatus status,
915e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)    sync_file_system::SyncAction action,
9203b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)    sync_file_system::SyncDirection direction) {
9303b57e008b61dfcb1fbad3aea950ae0e001748b0Torne (Richard Coles)}
945e3f23d412006dc4db4e659864679f29341e113fTorne (Richard Coles)
95cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void SyncFileSystemInternalsHandler::OnLogRecorded(
96cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const sync_file_system::TaskLogger::TaskLog& task_log) {
97cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  base::DictionaryValue dict;
98cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  int64 duration = (task_log.end_time - task_log.start_time).InMilliseconds();
99cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  dict.SetInteger("duration", duration);
100cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  dict.SetString("task_description", task_log.task_description);
101cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  dict.SetString("result_description", task_log.result_description);
102cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
103cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  scoped_ptr<base::ListValue> details(new base::ListValue);
104cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  details->AppendStrings(task_log.details);
105cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  dict.Set("details", details.release());
10646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  web_ui()->CallJavascriptFunction("TaskLog.onTaskLogRecorded", dict);
107cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
108cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
109b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)void SyncFileSystemInternalsHandler::GetServiceStatus(
110b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)    const base::ListValue* args) {
111effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  SyncServiceState state_enum = sync_file_system::SYNC_SERVICE_DISABLED;
112effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  sync_file_system::SyncFileSystemService* sync_service =
113effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch      SyncFileSystemServiceFactory::GetForProfile(profile_);
114effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (sync_service)
115effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    state_enum = sync_service->GetSyncServiceState();
116effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  const std::string state_string = extensions::api::sync_file_system::ToString(
117b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)      extensions::SyncServiceStateToExtensionEnum(state_enum));
1187d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  web_ui()->CallJavascriptFunction("SyncService.onGetServiceStatus",
119b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)                                   base::StringValue(state_string));
120b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}
121b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)
12290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SyncFileSystemInternalsHandler::GetNotificationSource(
12390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const base::ListValue* args) {
124eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  drive::DriveNotificationManager* drive_notification_manager =
125e5d81f57cb97b3b6b7fccc9c5610d21eb81db09dBen Murdoch      drive::DriveNotificationManagerFactory::FindForBrowserContext(profile_);
126effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch  if (!drive_notification_manager)
127effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    return;
12890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  bool xmpp_enabled = drive_notification_manager->push_notification_enabled();
12990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  std::string notification_source = xmpp_enabled ? "XMPP" : "Polling";
1307d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  web_ui()->CallJavascriptFunction("SyncService.onGetNotificationSource",
13190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)                                   base::StringValue(notification_source));
13290dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
13390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
13490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)void SyncFileSystemInternalsHandler::GetLog(
13590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    const base::ListValue* args) {
13690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  const std::vector<EventLogger::Event> log =
13790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      sync_file_system::util::GetLogHistory();
13890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1397dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  int last_log_id_sent;
1407dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch  if (!args->GetInteger(0, &last_log_id_sent))
1417dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    last_log_id_sent = -1;
1427dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch
14390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  // Collate events which haven't been sent to WebUI yet.
14490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  base::ListValue list;
14590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  for (std::vector<EventLogger::Event>::const_iterator log_entry = log.begin();
14690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       log_entry != log.end();
14790dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)       ++log_entry) {
1487dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    if (log_entry->id <= last_log_id_sent)
14990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)      continue;
15090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::DictionaryValue* dict = new base::DictionaryValue;
1527dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    dict->SetInteger("id", log_entry->id);
15390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    dict->SetString("time",
15490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)        google_apis::util::FormatTimeAsStringLocaltime(log_entry->when));
15590dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    dict->SetString("logEvent", log_entry->what);
15690dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    list.Append(dict);
1577dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch    last_log_id_sent = log_entry->id;
15890dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  }
15990dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)  if (list.empty())
16090dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)    return;
16190dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
1627d4cd473f85ac64c3747c96c277f9e506a0d2246Torne (Richard Coles)  web_ui()->CallJavascriptFunction("SyncService.onGetLog", list);
16390dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)}
16490dce4d38c5ff5333bea97d859d4e484e27edf0cTorne (Richard Coles)
165a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)void SyncFileSystemInternalsHandler::ClearLogs(const base::ListValue* args) {
166a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)  sync_file_system::util::ClearLog();
167a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)}
168a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7Torne (Richard Coles)
169cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)void SyncFileSystemInternalsHandler::ObserveTaskLog(
170cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const base::ListValue* args) {
171cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  sync_file_system::SyncFileSystemService* sync_service =
172cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      SyncFileSystemServiceFactory::GetForProfile(profile_);
173cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!sync_service)
174cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return;
175cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  if (!observing_task_log_) {
176cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    observing_task_log_ = true;
177cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    sync_service->task_logger()->AddObserver(this);
178cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
179cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
18046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  DCHECK(sync_service->task_logger());
181cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const sync_file_system::TaskLogger::LogList& log =
182cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      sync_service->task_logger()->GetLog();
18346d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)
184cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  for (sync_file_system::TaskLogger::LogList::const_iterator itr = log.begin();
185cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)       itr != log.end(); ++itr)
186cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    OnLogRecorded(**itr);
187cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)}
188cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
189b2df76ea8fec9e32f6f3718986dba0d95315b29cTorne (Richard Coles)}  // namespace syncfs_internals
190