Volume.cpp revision bf04185c88161f13118b9975cdff7967d49a4fa0
1/*
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
17#include <stdlib.h>
18#include <string.h>
19#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>
27#include <sys/mount.h>
28
29#include <linux/kdev_t.h>
30
31#include <cutils/properties.h>
32
33#include "diskmbr.h"
34
35#define LOG_TAG "Vold"
36
37#include <cutils/log.h>
38
39#include "Volume.h"
40#include "VolumeManager.h"
41#include "ResponseCode.h"
42#include "Fat.h"
43
44extern "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);
47
48static 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;
75    mLabel = strdup(label);
76    mMountpoint = strdup(mount_point);
77    mState = Volume::State_Init;
78    mCurrentlyMountedKdev = -1;
79}
80
81Volume::~Volume() {
82    free(mLabel);
83    free(mMountpoint);
84}
85
86dev_t Volume::getDiskDevice() {
87    return MKDEV(0, 0);
88};
89
90void Volume::handleVolumeShared() {
91}
92
93void Volume::handleVolumeUnshared() {
94}
95
96int Volume::handleBlockEvent(NetlinkEvent *evt) {
97    errno = ENOSYS;
98    return -1;
99}
100
101void Volume::setState(int state) {
102    char msg[255];
103    int oldState = mState;
104
105    if (oldState == state) {
106        LOGW("Duplicate state (%d)\n", state);
107        return;
108    }
109
110    mState = state;
111
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);
121}
122
123int 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
134int Volume::formatVol() {
135
136    if (getState() == Volume::State_NoMedia) {
137        errno = ENODEV;
138        return -1;
139    } else if (getState() != Volume::State_Idle) {
140        errno = EBUSY;
141        return -1;
142    }
143
144    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;
149        return -1;
150    }
151
152    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;
164    }
165
166    sprintf(devicePath, "/dev/block/vold/%d:%d",
167            MAJOR(partNode), MINOR(partNode));
168
169    LOGI("Volume %s (%s) being formatted", getLabel(), devicePath);
170
171    if (Fat::format(devicePath)) {
172        LOGE("Failed to format (%s)", strerror(errno));
173        goto err;
174    }
175
176    LOGI("Volume %s (%s) formatted sucessfully", getLabel(), devicePath);
177    return 0;
178err:
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;
249        setState(Volume::State_Checking);
250
251        if ((rc = Fat::check(devicePath))) {
252            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;
271        if (!(rc = Fat::doMount(devicePath, getMountpoint()))) {
272            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
300int 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}
388