aboutsummaryrefslogtreecommitdiff
path: root/brillo/blkdev_utils/loop_device_fake.cc
blob: a181aad67099aa8081ae2f22a1e7743cc5aa2815 (plain)
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
// Copyright 2018 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <brillo/blkdev_utils/loop_device_fake.h>

#include <linux/loop.h>
#include <memory>
#include <string>
#include <vector>

#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
#include <base/strings/stringprintf.h>
#include <brillo/blkdev_utils/loop_device.h>

// Not a loop ioctl: we only use this to get the backing file from
// the stubbed function. All loop device ioctls start with 0x4c.
#define LOOP_GET_DEV 0x4cff

namespace brillo {
namespace fake {

namespace {

int ParseLoopDeviceNumber(const base::FilePath& device_path) {
  int device_number;
  std::string path_string = device_path.value();
  return base::StartsWith(path_string, "/dev/loop",
                          base::CompareCase::SENSITIVE) &&
                 base::StringToInt(path_string.substr(9), &device_number)
             ? device_number
             : -1;
}

base::FilePath GetLoopDevicePath(int device_number) {
  return base::FilePath(base::StringPrintf("/dev/loop%d", device_number));
}

int StubIoctlRunner(const base::FilePath& path,
                    int type,
                    uint64_t arg,
                    int flag) {
  int device_number = ParseLoopDeviceNumber(path);
  struct loop_info64* info;
  struct LoopDev* device;
  static std::vector<struct LoopDev>& loop_device_vector =
      *new std::vector<struct LoopDev>();

  switch (type) {
    case LOOP_GET_STATUS64:
      if (loop_device_vector.size() <= device_number ||
          loop_device_vector[device_number].valid == false)
        return -1;
      info = reinterpret_cast<struct loop_info64*>(arg);
      memcpy(info, &loop_device_vector[device_number].info,
             sizeof(struct loop_info64));
      return 0;
    case LOOP_SET_STATUS64:
      if (loop_device_vector.size() <= device_number ||
          loop_device_vector[device_number].valid == false)
        return -1;
      info = reinterpret_cast<struct loop_info64*>(arg);
      memcpy(&loop_device_vector[device_number].info, info,
             sizeof(struct loop_info64));
      return 0;
    case LOOP_CLR_FD:
      if (loop_device_vector.size() <= device_number ||
          loop_device_vector[device_number].valid == false)
        return -1;
      loop_device_vector[device_number].valid = false;
      return 0;
    case LOOP_CTL_GET_FREE:
      device_number = loop_device_vector.size();
      loop_device_vector.push_back({true, base::FilePath(), {0}});
      return device_number;
    // Instead of passing the fd here, we pass the FilePath of the backing
    // file.
    case LOOP_SET_FD:
      if (loop_device_vector.size() <= device_number)
        return -1;
      loop_device_vector[device_number].backing_file =
          *reinterpret_cast<const base::FilePath*>(arg);
      return 0;
    // Not a loop ioctl; Only used for conveniently checking the
    // validity of the loop devices.
    case LOOP_GET_DEV:
      if (device_number >= loop_device_vector.size())
        return -1;
      device = reinterpret_cast<struct LoopDev*>(arg);
      device->valid = loop_device_vector[device_number].valid;
      device->backing_file = loop_device_vector[device_number].backing_file;
      memset(&(device->info), 0, sizeof(struct loop_info64));
      return 0;
    default:
      return -1;
  }
}

}  // namespace

FakeLoopDeviceManager::FakeLoopDeviceManager()
    : LoopDeviceManager(base::Bind(&StubIoctlRunner)) {}

std::unique_ptr<LoopDevice> FakeLoopDeviceManager::AttachDeviceToFile(
    const base::FilePath& backing_file) {
  int device_number = StubIoctlRunner(base::FilePath("/dev/loop-control"),
                                      LOOP_CTL_GET_FREE, 0, 0);

  if (StubIoctlRunner(GetLoopDevicePath(device_number), LOOP_SET_FD,
                      reinterpret_cast<uint64_t>(&backing_file), 0) < 0)
    return std::make_unique<LoopDevice>(-1, base::FilePath(),
                                        base::Bind(&StubIoctlRunner));

  return std::make_unique<LoopDevice>(device_number, backing_file,
                                      base::Bind(&StubIoctlRunner));
}

std::vector<std::unique_ptr<LoopDevice>>
FakeLoopDeviceManager::SearchLoopDevicePaths(int device_number) {
  std::vector<std::unique_ptr<LoopDevice>> devices;
  struct LoopDev device;

  if (device_number != -1) {
    if (StubIoctlRunner(GetLoopDevicePath(device_number), LOOP_GET_DEV,
                        reinterpret_cast<uint64_t>(&device), 0) < 0)
      return devices;

    if (device.valid)
      devices.push_back(std::make_unique<LoopDevice>(
          device_number, device.backing_file, base::Bind(&StubIoctlRunner)));
    return devices;
  }

  int i = 0;
  while (StubIoctlRunner(GetLoopDevicePath(i), LOOP_GET_DEV,
                         reinterpret_cast<uint64_t>(&device), 0) == 0) {
    if (device.valid)
      devices.push_back(std::make_unique<LoopDevice>(
          i, device.backing_file, base::Bind(&StubIoctlRunner)));
    i++;
  }
  return devices;
}

}  // namespace fake
}  // namespace brillo