1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229

base / system / sys_info_fuchsia.cc [blame]

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "base/system/sys_info.h"

#include <fidl/fuchsia.buildinfo/cpp/fidl.h>
#include <fidl/fuchsia.hwinfo/cpp/fidl.h>
#include <sys/statvfs.h>
#include <zircon/syscalls.h>

#include <string>

#include "base/containers/flat_map.h"
#include "base/files/file_util.h"
#include "base/fuchsia/fuchsia_logging.h"
#include "base/fuchsia/system_info.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/notimplemented.h"
#include "base/numerics/clamped_math.h"
#include "base/synchronization/lock.h"
#include "base/threading/scoped_blocking_call.h"
#include "build/build_config.h"

namespace base {

namespace {

bool GetDiskSpaceInfo(const FilePath& path,
                      int64_t* available_bytes,
                      int64_t* total_bytes) {
  struct statvfs stats;
  if (statvfs(path.value().c_str(), &stats) != 0) {
    PLOG(ERROR) << "statvfs() for path:" << path;
    return false;
  }

  if (available_bytes) {
    ClampedNumeric<int64_t> available_blocks(stats.f_bavail);
    *available_bytes = available_blocks * stats.f_frsize;
  }

  if (total_bytes) {
    ClampedNumeric<int64_t> total_blocks(stats.f_blocks);
    *total_bytes = total_blocks * stats.f_frsize;
  }

  return true;
}

struct TotalDiskSpace {
  Lock lock;
  flat_map<FilePath, int64_t> space_map GUARDED_BY(lock);
};

TotalDiskSpace& GetTotalDiskSpace() {
  static NoDestructor<TotalDiskSpace> total_disk_space;
  return *total_disk_space;
}

// Returns the total-disk-space set for the volume containing |path|. If
// |volume_path| is non-null then it receives the path to the relevant volume.
// Returns -1, and does not modify |volume_path|, if no match is found. Also
// returns -1 if |path| is not absolute.
int64_t GetAmountOfTotalDiskSpaceAndVolumePath(const FilePath& path,
                                               FilePath* volume_path) {
  if (!path.IsAbsolute()) {
    return -1;
  }
  TotalDiskSpace& total_disk_space = GetTotalDiskSpace();

  AutoLock l(total_disk_space.lock);
  int64_t result = -1;
  FilePath matched_path;
  for (const auto& path_and_size : total_disk_space.space_map) {
    if (path_and_size.first == path || path_and_size.first.IsParent(path)) {
      // If a deeper path was already matched then ignore this entry.
      if (!matched_path.empty() && !matched_path.IsParent(path_and_size.first))
        continue;
      matched_path = path_and_size.first;
      result = path_and_size.second;
    }
  }

  if (volume_path)
    *volume_path = matched_path;
  return result;
}

}  // namespace

// static
uint64_t SysInfo::AmountOfPhysicalMemoryImpl() {
  return zx_system_get_physmem();
}

// static
uint64_t SysInfo::AmountOfAvailablePhysicalMemoryImpl() {
  // TODO(crbug.com/42050649): Implement this when Fuchsia supports it.
  NOTIMPLEMENTED_LOG_ONCE();
  return 0;
}

// static
int SysInfo::NumberOfProcessors() {
  return static_cast<int>(zx_system_get_num_cpus());
}

// static
uint64_t SysInfo::AmountOfVirtualMemory() {
  // Fuchsia does not provide this type of information.
  // Return zero to indicate that there is unlimited available virtual memory.
  return 0;
}

// static
std::string SysInfo::OperatingSystemName() {
  return "Fuchsia";
}

// static
int64_t SysInfo::AmountOfFreeDiskSpace(const FilePath& path) {
  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);

  // First check whether there is a soft-quota that applies to |path|.
  FilePath volume_path;
  const int64_t total_space =
      GetAmountOfTotalDiskSpaceAndVolumePath(path, &volume_path);
  if (total_space >= 0) {
    // TODO(crbug.com/42050202): Replace this with an efficient implementation.
    const int64_t used_space = ComputeDirectorySize(volume_path);
    return std::max(0l, total_space - used_space);
  }

  // Report the actual amount of free space in |path|'s filesystem.
  int64_t available;
  if (GetDiskSpaceInfo(path, &available, nullptr))
    return available;

  return -1;
}

// static
int64_t SysInfo::AmountOfTotalDiskSpace(const FilePath& path) {
  ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);

  if (path.empty())
    return -1;

  // Return the soft-quota that applies to |path|, if one is configured.
  int64_t total_space = GetAmountOfTotalDiskSpaceAndVolumePath(path, nullptr);
  if (total_space >= 0)
    return total_space;

  // Report the actual space in |path|'s filesystem.
  if (GetDiskSpaceInfo(path, nullptr, &total_space))
    return total_space;

  return -1;
}

// static
void SysInfo::SetAmountOfTotalDiskSpace(const FilePath& path, int64_t bytes) {
  DCHECK(path.IsAbsolute());
  TotalDiskSpace& total_disk_space = GetTotalDiskSpace();
  AutoLock l(total_disk_space.lock);
  if (bytes >= 0)
    total_disk_space.space_map[path] = bytes;
  else
    total_disk_space.space_map.erase(path);
}

// static
std::string SysInfo::OperatingSystemVersion() {
  const auto& build_info = GetCachedBuildInfo();
  return build_info.version().value_or("");
}

// static
void SysInfo::OperatingSystemVersionNumbers(int32_t* major_version,
                                            int32_t* minor_version,
                                            int32_t* bugfix_version) {
  // TODO(crbug.com/42050501): Implement this when Fuchsia supports it.
  NOTIMPLEMENTED_LOG_ONCE();
  *major_version = 0;
  *minor_version = 0;
  *bugfix_version = 0;
}

// static
std::string SysInfo::OperatingSystemArchitecture() {
#if defined(ARCH_CPU_X86_64)
  return "x86_64";
#elif defined(ARCH_CPU_ARM64)
  return "aarch64";
#else
#error Unsupported architecture.
#endif
}

// static
std::string SysInfo::CPUModelName() {
  // TODO(crbug.com/40191727): Implement this when Fuchsia supports it.
  NOTIMPLEMENTED_LOG_ONCE();
  return std::string();
}

// static
size_t SysInfo::VMAllocationGranularity() {
  return static_cast<size_t>(getpagesize());
}

// static
int SysInfo::NumberOfEfficientProcessorsImpl() {
  NOTIMPLEMENTED();
  return 0;
}

SysInfo::HardwareInfo SysInfo::GetHardwareInfoSync() {
  const auto product_info = GetProductInfo();

  return {
      .manufacturer = product_info.manufacturer().value_or(""),
      .model = product_info.model().value_or(""),
  };
}

}  // namespace base