blob: 6926d77f66ce3ee805fc8d68f31c7bf199d5d045 [file] [log] [blame]
San Mehatf1b736b2009-10-10 17:22:08 -07001/*
2 * Copyright (C) 2008 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
San Mehat49e2bce2009-10-12 16:29:01 -070017#include <stdlib.h>
San Mehatf1b736b2009-10-10 17:22:08 -070018#include <string.h>
San Mehat49e2bce2009-10-12 16:29:01 -070019#include <dirent.h>
20#include <errno.h>
21#include <fcntl.h>
22
23#include <sys/types.h>
24#include <sys/stat.h>
25#include <sys/types.h>
26#include <sys/mman.h>
San Mehata2677e42009-12-13 10:40:18 -080027#include <sys/mount.h>
28
29#include <linux/kdev_t.h>
30
31#include <cutils/properties.h>
32
33#include "diskmbr.h"
San Mehatf1b736b2009-10-10 17:22:08 -070034
35#define LOG_TAG "Vold"
36
37#include <cutils/log.h>
38
39#include "Volume.h"
San Mehata2677e42009-12-13 10:40:18 -080040#include "VolumeManager.h"
41#include "ResponseCode.h"
San Mehatbf041852010-01-04 10:09:16 -080042#include "Fat.h"
San Mehatf1b736b2009-10-10 17:22:08 -070043
San Mehata2677e42009-12-13 10:40:18 -080044extern "C" void KillProcessesWithOpenFiles(const char *, int, int, int);
45extern "C" void dos_partition_dec(void const *pp, struct dos_partition *d);
46extern "C" void dos_partition_enc(void *pp, struct dos_partition *d);
San Mehat49e2bce2009-10-12 16:29:01 -070047
San Mehata2677e42009-12-13 10:40:18 -080048static const char *stateToStr(int state) {
49 if (state == Volume::State_Init)
50 return "Initializing";
51 else if (state == Volume::State_NoMedia)
52 return "No-Media";
53 else if (state == Volume::State_Idle)
54 return "Idle-Unmounted";
55 else if (state == Volume::State_Pending)
56 return "Pending";
57 else if (state == Volume::State_Mounted)
58 return "Mounted";
59 else if (state == Volume::State_Unmounting)
60 return "Unmounting";
61 else if (state == Volume::State_Checking)
62 return "Checking";
63 else if (state == Volume::State_Formatting)
64 return "Formatting";
65 else if (state == Volume::State_Shared)
66 return "Shared-Unmounted";
67 else if (state == Volume::State_SharedMnt)
68 return "Shared-Mounted";
69 else
70 return "Unknown-Error";
71}
72
73Volume::Volume(VolumeManager *vm, const char *label, const char *mount_point) {
74 mVm = vm;
San Mehatf1b736b2009-10-10 17:22:08 -070075 mLabel = strdup(label);
76 mMountpoint = strdup(mount_point);
77 mState = Volume::State_Init;
San Mehata2677e42009-12-13 10:40:18 -080078 mCurrentlyMountedKdev = -1;
San Mehatf1b736b2009-10-10 17:22:08 -070079}
80
81Volume::~Volume() {
82 free(mLabel);
83 free(mMountpoint);
84}
85
San Mehata2677e42009-12-13 10:40:18 -080086dev_t Volume::getDiskDevice() {
87 return MKDEV(0, 0);
88};
89
90void Volume::handleVolumeShared() {
91}
92
93void Volume::handleVolumeUnshared() {
94}
95
San Mehatfd7f5872009-10-12 11:32:47 -070096int Volume::handleBlockEvent(NetlinkEvent *evt) {
San Mehatf1b736b2009-10-10 17:22:08 -070097 errno = ENOSYS;
98 return -1;
99}
100
101void Volume::setState(int state) {
San Mehata2677e42009-12-13 10:40:18 -0800102 char msg[255];
103 int oldState = mState;
104
105 if (oldState == state) {
106 LOGW("Duplicate state (%d)\n", state);
107 return;
108 }
109
San Mehatf1b736b2009-10-10 17:22:08 -0700110 mState = state;
San Mehata2677e42009-12-13 10:40:18 -0800111
112 LOGD("Volume %s state changing %d (%s) -> %d (%s)", mLabel,
113 oldState, stateToStr(oldState), mState, stateToStr(mState));
114 snprintf(msg, sizeof(msg),
115 "Volume %s %s state changed from %d (%s) to %d (%s)", getLabel(),
116 getMountpoint(), oldState, stateToStr(oldState), mState,
117 stateToStr(mState));
118
119 mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
120 msg, false);
San Mehatf1b736b2009-10-10 17:22:08 -0700121}
San Mehat49e2bce2009-10-12 16:29:01 -0700122
San Mehatdd9b8e92009-10-21 11:06:52 -0700123int Volume::createDeviceNode(const char *path, int major, int minor) {
124 mode_t mode = 0660 | S_IFBLK;
125 dev_t dev = (major << 8) | minor;
126 if (mknod(path, mode, dev) < 0) {
127 if (errno != EEXIST) {
128 return -1;
129 }
130 }
131 return 0;
132}
133
San Mehata2677e42009-12-13 10:40:18 -0800134int Volume::formatVol() {
San Mehat49e2bce2009-10-12 16:29:01 -0700135
San Mehata2677e42009-12-13 10:40:18 -0800136 if (getState() == Volume::State_NoMedia) {
137 errno = ENODEV;
138 return -1;
139 } else if (getState() != Volume::State_Idle) {
140 errno = EBUSY;
San Mehat49e2bce2009-10-12 16:29:01 -0700141 return -1;
142 }
143
San Mehata2677e42009-12-13 10:40:18 -0800144 if (isMountpointMounted(getMountpoint())) {
145 LOGW("Volume is idle but appears to be mounted - fixing");
146 setState(Volume::State_Mounted);
147 // mCurrentlyMountedKdev = XXX
148 errno = EBUSY;
San Mehat49e2bce2009-10-12 16:29:01 -0700149 return -1;
150 }
151
San Mehata2677e42009-12-13 10:40:18 -0800152 char devicePath[255];
153 dev_t diskNode = getDiskDevice();
154 dev_t partNode = MKDEV(MAJOR(diskNode), 1); // XXX: Hmmm
155
156 sprintf(devicePath, "/dev/block/vold/%d:%d",
157 MAJOR(diskNode), MINOR(diskNode));
158
159 LOGI("Volume %s (%s) MBR being initialized", getLabel(), devicePath);
160
161 if (initializeMbr(devicePath)) {
162 LOGE("Failed to initialize MBR (%s)", strerror(errno));
163 goto err;
San Mehat49e2bce2009-10-12 16:29:01 -0700164 }
165
San Mehata2677e42009-12-13 10:40:18 -0800166 sprintf(devicePath, "/dev/block/vold/%d:%d",
167 MAJOR(partNode), MINOR(partNode));
San Mehatdd9b8e92009-10-21 11:06:52 -0700168
San Mehata2677e42009-12-13 10:40:18 -0800169 LOGI("Volume %s (%s) being formatted", getLabel(), devicePath);
170
San Mehatbf041852010-01-04 10:09:16 -0800171 if (Fat::format(devicePath)) {
San Mehata2677e42009-12-13 10:40:18 -0800172 LOGE("Failed to format (%s)", strerror(errno));
173 goto err;
174 }
175
176 LOGI("Volume %s (%s) formatted sucessfully", getLabel(), devicePath);
San Mehat49e2bce2009-10-12 16:29:01 -0700177 return 0;
San Mehata2677e42009-12-13 10:40:18 -0800178err:
179 return -1;
180}
181
182bool Volume::isMountpointMounted(const char *path) {
183 char device[256];
184 char mount_path[256];
185 char rest[256];
186 FILE *fp;
187 char line[1024];
188
189 if (!(fp = fopen("/proc/mounts", "r"))) {
190 LOGE("Error opening /proc/mounts (%s)", strerror(errno));
191 return false;
192 }
193
194 while(fgets(line, sizeof(line), fp)) {
195 line[strlen(line)-1] = '\0';
196 sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
197 if (!strcmp(mount_path, path)) {
198 fclose(fp);
199 return true;
200 }
201
202 }
203
204 fclose(fp);
205 return false;
206}
207
208int Volume::mountVol() {
209 dev_t deviceNodes[4];
210 int n, i, rc = 0;
211 char errmsg[255];
212
213 if (getState() == Volume::State_NoMedia) {
214 snprintf(errmsg, sizeof(errmsg),
215 "Volume %s %s mount failed - no media",
216 getLabel(), getMountpoint());
217 mVm->getBroadcaster()->sendBroadcast(
218 ResponseCode::VolumeMountFailedNoMedia,
219 errmsg, false);
220 errno = ENODEV;
221 return -1;
222 } else if (getState() != Volume::State_Idle) {
223 errno = EBUSY;
224 return -1;
225 }
226
227 if (isMountpointMounted(getMountpoint())) {
228 LOGW("Volume is idle but appears to be mounted - fixing");
229 setState(Volume::State_Mounted);
230 // mCurrentlyMountedKdev = XXX
231 return 0;
232 }
233
234 n = getDeviceNodes((dev_t *) &deviceNodes, 4);
235 if (!n) {
236 LOGE("Failed to get device nodes (%s)\n", strerror(errno));
237 return -1;
238 }
239
240 for (i = 0; i < n; i++) {
241 char devicePath[255];
242
243 sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
244 MINOR(deviceNodes[i]));
245
246 LOGI("%s being considered for volume %s\n", devicePath, getLabel());
247
248 errno = 0;
San Mehatbf041852010-01-04 10:09:16 -0800249 setState(Volume::State_Checking);
250
251 if ((rc = Fat::check(devicePath))) {
San Mehata2677e42009-12-13 10:40:18 -0800252 if (errno == ENODATA) {
253 LOGW("%s does not contain a FAT filesystem\n", devicePath);
254 continue;
255 } else {
256 /* Badness - abort the mount */
257 LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
258 snprintf(errmsg, sizeof(errmsg),
259 "Volume %s %s mount failed - filesystem check failed",
260 getLabel(), getMountpoint());
261 mVm->getBroadcaster()->sendBroadcast(
262 ResponseCode::VolumeMountFailedDamaged,
263 errmsg, false);
264 setState(Volume::State_Idle);
265 goto out;
266 }
267 }
268
269 LOGI("%s checks out - attempting to mount\n", devicePath);
270 errno = 0;
San Mehatfff0b472010-01-06 19:19:46 -0800271 if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false,
272 1000, 1015, 0702, true))) {
San Mehata2677e42009-12-13 10:40:18 -0800273 LOGI("%s sucessfully mounted for volume %s\n", devicePath,
274 getLabel());
275 setState(Volume::State_Mounted);
276 mCurrentlyMountedKdev = deviceNodes[i];
277 goto out;
278 }
279
280 LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
281 }
282
283 // XXX: Doesn't handle multiple partitions properly
284 if (errno == ENODATA) {
285 snprintf(errmsg, sizeof(errmsg),
286 "Volume %s %s mount failed - no supported file-systems",
287 getLabel(), getMountpoint());
288 mVm->getBroadcaster()->sendBroadcast(
289 ResponseCode::VolumeMountFailedBlank,
290 errmsg, false);
291 }
292
293
294 LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
295 setState(Volume::State_Idle);
296
297out:
298 return rc;
299}
300
San Mehata2677e42009-12-13 10:40:18 -0800301int Volume::unmountVol() {
302 int i, rc;
303
304 if (getState() != Volume::State_Mounted) {
305 LOGE("Volume %s unmount request when not mounted", getLabel());
306 errno = EINVAL;
307 return -1;
308 }
309
310 setState(Volume::State_Unmounting);
311 for (i = 0; i < 10; i++) {
312 rc = umount(getMountpoint());
313 if (!rc)
314 break;
315
316 if (rc && (errno == EINVAL || errno == ENOENT)) {
317 rc = 0;
318 break;
319 }
320
321 LOGW("Volume %s unmount attempt %d failed (%s)",
322 getLabel(), i + 1, strerror(errno));
323
324 if (i < 5) {
325 usleep(1000 * 250);
326 } else {
327 KillProcessesWithOpenFiles(getMountpoint(),
328 (i < 7 ? 0 : 1),
329 NULL, 0);
330 usleep(1000 * 250);
331 }
332 }
333
334 if (!rc) {
335 LOGI("Volume %s unmounted sucessfully", getLabel());
336 setState(Volume::State_Idle);
337 mCurrentlyMountedKdev = -1;
338 return 0;
339 }
340
341 LOGE("Volume %s failed to unmount (%s)\n", getLabel(), strerror(errno));
342 setState(Volume::State_Mounted);
343 return -1;
344}
345
346int Volume::initializeMbr(const char *deviceNode) {
347 int fd, rc;
348 unsigned char block[512];
349 struct dos_partition part;
350 unsigned int nr_sec;
351
352 if ((fd = open(deviceNode, O_RDWR)) < 0) {
353 LOGE("Error opening disk file (%s)", strerror(errno));
354 return -1;
355 }
356
357 if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
358 LOGE("Unable to get device size (%s)", strerror(errno));
359 close(fd);
360 return -1;
361 }
362
363 memset(&part, 0, sizeof(part));
364 part.dp_flag = 0x80;
365 part.dp_typ = 0xc;
366 part.dp_start = ((1024 * 64) / 512) + 1;
367 part.dp_size = nr_sec - part.dp_start;
368
369 memset(block, 0, sizeof(block));
370 block[0x1fe] = 0x55;
371 block[0x1ff] = 0xaa;
372
373 dos_partition_enc(block + DOSPARTOFF, &part);
374
375 if (write(fd, block, sizeof(block)) < 0) {
376 LOGE("Error writing MBR (%s)", strerror(errno));
377 close(fd);
378 return -1;
379 }
380
381 if (ioctl(fd, BLKRRPART, NULL) < 0) {
382 LOGE("Error re-reading partition table (%s)", strerror(errno));
383 close(fd);
384 return -1;
385 }
386 close(fd);
387 return 0;
388}