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