blob: 787d4cd3cc4c3ea882a4a777c11ae572ad77b2da [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 Mehata19b2502010-01-06 10:33:53 -0800271 if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false))) {
San Mehata2677e42009-12-13 10:40:18 -0800272 LOGI("%s sucessfully mounted for volume %s\n", devicePath,
273 getLabel());
274 setState(Volume::State_Mounted);
275 mCurrentlyMountedKdev = deviceNodes[i];
276 goto out;
277 }
278
279 LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
280 }
281
282 // XXX: Doesn't handle multiple partitions properly
283 if (errno == ENODATA) {
284 snprintf(errmsg, sizeof(errmsg),
285 "Volume %s %s mount failed - no supported file-systems",
286 getLabel(), getMountpoint());
287 mVm->getBroadcaster()->sendBroadcast(
288 ResponseCode::VolumeMountFailedBlank,
289 errmsg, false);
290 }
291
292
293 LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
294 setState(Volume::State_Idle);
295
296out:
297 return rc;
298}
299
San Mehata2677e42009-12-13 10:40:18 -0800300int Volume::unmountVol() {
301 int i, rc;
302
303 if (getState() != Volume::State_Mounted) {
304 LOGE("Volume %s unmount request when not mounted", getLabel());
305 errno = EINVAL;
306 return -1;
307 }
308
309 setState(Volume::State_Unmounting);
310 for (i = 0; i < 10; i++) {
311 rc = umount(getMountpoint());
312 if (!rc)
313 break;
314
315 if (rc && (errno == EINVAL || errno == ENOENT)) {
316 rc = 0;
317 break;
318 }
319
320 LOGW("Volume %s unmount attempt %d failed (%s)",
321 getLabel(), i + 1, strerror(errno));
322
323 if (i < 5) {
324 usleep(1000 * 250);
325 } else {
326 KillProcessesWithOpenFiles(getMountpoint(),
327 (i < 7 ? 0 : 1),
328 NULL, 0);
329 usleep(1000 * 250);
330 }
331 }
332
333 if (!rc) {
334 LOGI("Volume %s unmounted sucessfully", getLabel());
335 setState(Volume::State_Idle);
336 mCurrentlyMountedKdev = -1;
337 return 0;
338 }
339
340 LOGE("Volume %s failed to unmount (%s)\n", getLabel(), strerror(errno));
341 setState(Volume::State_Mounted);
342 return -1;
343}
344
345int Volume::initializeMbr(const char *deviceNode) {
346 int fd, rc;
347 unsigned char block[512];
348 struct dos_partition part;
349 unsigned int nr_sec;
350
351 if ((fd = open(deviceNode, O_RDWR)) < 0) {
352 LOGE("Error opening disk file (%s)", strerror(errno));
353 return -1;
354 }
355
356 if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
357 LOGE("Unable to get device size (%s)", strerror(errno));
358 close(fd);
359 return -1;
360 }
361
362 memset(&part, 0, sizeof(part));
363 part.dp_flag = 0x80;
364 part.dp_typ = 0xc;
365 part.dp_start = ((1024 * 64) / 512) + 1;
366 part.dp_size = nr_sec - part.dp_start;
367
368 memset(block, 0, sizeof(block));
369 block[0x1fe] = 0x55;
370 block[0x1ff] = 0xaa;
371
372 dos_partition_enc(block + DOSPARTOFF, &part);
373
374 if (write(fd, block, sizeof(block)) < 0) {
375 LOGE("Error writing MBR (%s)", strerror(errno));
376 close(fd);
377 return -1;
378 }
379
380 if (ioctl(fd, BLKRRPART, NULL) < 0) {
381 LOGE("Error re-reading partition table (%s)", strerror(errno));
382 close(fd);
383 return -1;
384 }
385 close(fd);
386 return 0;
387}