blob: 673827c84916b18762bbcebf9ede421bd2386e65 [file] [log] [blame]
Jeff Sharkeydeb24052015-03-02 21:01:40 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "Vold"
18
19#include "Disk.h"
20#include "PublicVolume.h"
21#include "Utils.h"
22#include "VolumeBase.h"
23
24#include <cutils/log.h>
25#include <diskconfig/diskconfig.h>
26#include <utils/file.h>
27#include <utils/stringprintf.h>
28
29#include <fcntl.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <sys/types.h>
33#include <sys/stat.h>
34#include <sys/mount.h>
35
36namespace android {
37namespace vold {
38
39static const char* kSgdiskPath = "/system/bin/sgdisk";
40static const char* kSgdiskToken = " \t\n";
41
42static const char* kSysfsMmcMaxMinors = "/sys/module/mmcblk/parameters/perdev_minors";
43
44static const unsigned int kMajorBlockScsi = 8;
45static const unsigned int kMajorBlockMmc = 179;
46
47static const char* kGptBasicData = "EBD0A0A2-B9E5-4433-87C0-68B6B72699C7";
48static const char* kGptAndroidMeta = "19A710A2-B3CA-11E4-B026-10604B889DCF";
49static const char* kGptAndroidExt = "193D1EA4-B3CA-11E4-B075-10604B889DCF";
50
51enum class Table {
52 kUnknown,
53 kMbr,
54 kGpt,
55};
56
57Disk::Disk(const std::string& eventPath, dev_t device) :
58 mDevice(device), mSize(-1) {
59 mId = StringPrintf("disk:%ud:%ud", major(device), minor(device));
60 mSysPath = StringPrintf("/sys/%s", eventPath.c_str());
61 mDevPath = StringPrintf("/dev/block/vold/%ud:%ud", major(device), minor(device));
62
63 CreateDeviceNode(mDevPath, mDevice);
64}
65
66Disk::~Disk() {
67 DestroyDeviceNode(mDevPath);
68}
69
70std::shared_ptr<VolumeBase> Disk::findVolume(const std::string& id) {
71 for (std::shared_ptr<VolumeBase>& v : mParts) {
72 if (!id.compare(v->getId())) {
73 return v;
74 }
75 }
76 return nullptr;
77}
78
79status_t Disk::readMetadata() {
80 mSize = -1;
81 mLabel = "";
82
83 {
84 std::string path(mSysPath + "/size");
85 std::string tmp;
86 if (!ReadFileToString(path, &tmp)) {
87 ALOGW("Failed to read size from %s: %s", path.c_str(), strerror(errno));
88 return -errno;
89 }
90 mSize = strtoll(tmp.c_str(), nullptr, 10);
91 }
92
93 switch (major(mDevice)) {
94 case kMajorBlockScsi: {
95 std::string path(mSysPath + "/device/vendor");
96 std::string tmp;
97 if (!ReadFileToString(path, &tmp)) {
98 ALOGW("Failed to read vendor from %s: %s", path.c_str(), strerror(errno));
99 return -errno;
100 }
101 mLabel = tmp;
102 break;
103 }
104 case kMajorBlockMmc: {
105 std::string path(mSysPath + "/device/manfid");
106 std::string tmp;
107 if (!ReadFileToString(path, &tmp)) {
108 ALOGW("Failed to read manufacturer from %s: %s", path.c_str(), strerror(errno));
109 return -errno;
110 }
111 uint64_t manfid = strtoll(tmp.c_str(), nullptr, 16);
112 // Our goal here is to give the user a meaningful label, ideally
113 // matching whatever is silk-screened on the card. To reduce
114 // user confusion, this list doesn't contain white-label manfid.
115 switch (manfid) {
116 case 0x000003: mLabel = "SanDisk"; break;
117 case 0x00001b: mLabel = "Samsung"; break;
118 case 0x000028: mLabel = "Lexar"; break;
119 case 0x000074: mLabel = "Transcend"; break;
120 }
121 break;
122 }
123 default: {
124 ALOGW("Unsupported block major type %d", major(mDevice));
125 return -ENOTSUP;
126 }
127 }
128
129 return OK;
130}
131
132status_t Disk::readPartitions() {
133 int8_t maxMinors = getMaxMinors();
134 if (maxMinors < 0) {
135 return -ENOTSUP;
136 }
137
138 mParts.clear();
139
140 // Parse partition table
141 std::string path(kSgdiskPath);
142 path += " --android-dump ";
143 path += mDevPath;
144 FILE* fp = popen(path.c_str(), "r");
145 if (!fp) {
146 ALOGE("Failed to run %s: %s", path.c_str(), strerror(errno));
147 return -errno;
148 }
149
150 char line[1024];
151 Table table = Table::kUnknown;
152 while (fgets(line, sizeof(line), fp) != nullptr) {
153 char* token = strtok(line, kSgdiskToken);
154 if (!strcmp(token, "DISK")) {
155 const char* type = strtok(nullptr, kSgdiskToken);
156 ALOGD("%s: found %s partition table", mId.c_str(), type);
157 if (!strcmp(type, "mbr")) {
158 table = Table::kMbr;
159 } else if (!strcmp(type, "gpt")) {
160 table = Table::kGpt;
161 }
162 } else if (!strcmp(token, "PART")) {
163 int i = strtol(strtok(nullptr, kSgdiskToken), nullptr, 10);
164 if (i <= 0 || i > maxMinors) {
165 ALOGW("%s: ignoring partition %d beyond max supported devices",
166 mId.c_str(), i);
167 continue;
168 }
169 dev_t partDevice = makedev(major(mDevice), minor(mDevice) + i);
170
171 VolumeBase* vol = nullptr;
172 if (table == Table::kMbr) {
173 const char* type = strtok(nullptr, kSgdiskToken);
174 ALOGD("%s: MBR partition %d type %s", mId.c_str(), i, type);
175
176 switch (strtol(type, nullptr, 16)) {
177 case 0x06: // FAT16
178 case 0x0b: // W95 FAT32 (LBA)
179 case 0x0c: // W95 FAT32 (LBA)
180 case 0x0e: // W95 FAT16 (LBA)
181 vol = new PublicVolume(partDevice);
182 break;
183 }
184 } else if (table == Table::kGpt) {
185 const char* typeGuid = strtok(nullptr, kSgdiskToken);
186 const char* partGuid = strtok(nullptr, kSgdiskToken);
187 ALOGD("%s: GPT partition %d type %s, GUID %s", mId.c_str(), i,
188 typeGuid, partGuid);
189
190 if (!strcasecmp(typeGuid, kGptBasicData)) {
191 vol = new PublicVolume(partDevice);
192 } else if (!strcasecmp(typeGuid, kGptAndroidExt)) {
193 //vol = new PrivateVolume();
194 }
195 }
196
197 if (vol != nullptr) {
198 mParts.push_back(std::shared_ptr<VolumeBase>(vol));
199 }
200 }
201 }
202
203 // Ugly last ditch effort, treat entire disk as partition
204 if (table == Table::kUnknown) {
205 ALOGD("%s: unknown partition table; trying entire device", mId.c_str());
206 VolumeBase* vol = new PublicVolume(mDevice);
207 mParts.push_back(std::shared_ptr<VolumeBase>(vol));
208 }
209
210 pclose(fp);
211 return OK;
212}
213
214status_t Disk::partitionPublic() {
215 // TODO: improve this code
216
217 struct disk_info dinfo;
218 memset(&dinfo, 0, sizeof(dinfo));
219
220 if (!(dinfo.part_lst = (struct part_info *) malloc(
221 MAX_NUM_PARTS * sizeof(struct part_info)))) {
222 SLOGE("Failed to malloc prt_lst");
223 return -1;
224 }
225
226 memset(dinfo.part_lst, 0, MAX_NUM_PARTS * sizeof(struct part_info));
227 dinfo.device = strdup(mDevPath.c_str());
228 dinfo.scheme = PART_SCHEME_MBR;
229 dinfo.sect_size = 512;
230 dinfo.skip_lba = 2048;
231 dinfo.num_lba = 0;
232 dinfo.num_parts = 1;
233
234 struct part_info *pinfo = &dinfo.part_lst[0];
235
236 pinfo->name = strdup("android_sdcard");
237 pinfo->flags |= PART_ACTIVE_FLAG;
238 pinfo->type = PC_PART_TYPE_FAT32;
239 pinfo->len_kb = -1;
240
241 int rc = apply_disk_config(&dinfo, 0);
242 if (rc) {
243 SLOGE("Failed to apply disk configuration (%d)", rc);
244 goto out;
245 }
246
247out:
248 free(pinfo->name);
249 free(dinfo.device);
250 free(dinfo.part_lst);
251
252 return rc;
253}
254
255status_t Disk::partitionPrivate() {
256 return -ENOTSUP;
257}
258
259status_t Disk::partitionMixed(int8_t ratio) {
260 return -ENOTSUP;
261}
262
263int Disk::getMaxMinors() {
264 // Figure out maximum partition devices supported
265 switch (major(mDevice)) {
266 case kMajorBlockScsi: {
267 // Per Documentation/devices.txt this is static
268 return 15;
269 }
270 case kMajorBlockMmc: {
271 // Per Documentation/devices.txt this is dynamic
272 std::string tmp;
273 if (!ReadFileToString(kSysfsMmcMaxMinors, &tmp)) {
274 ALOGW("Failed to read max minors");
275 return -errno;
276 }
277 return atoi(tmp.c_str());
278 }
279 }
280
281 ALOGW("Unsupported block major type %d", major(mDevice));
282 return -ENOTSUP;
283}
284
285} // namespace vold
286} // namespace android