Volume.cpp revision eba65e9d438a05f1c5dfd0f8d31bc463a5d08eee
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            }
256            errno = EIO;
257            /* Badness - abort the mount */
258            LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
259            setState(Volume::State_Idle);
260            return -1;
261        }
262
263        LOGI("%s checks out - attempting to mount\n", devicePath);
264        errno = 0;
265        if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false,
266                                1000, 1015, 0702, true))) {
267            LOGI("%s sucessfully mounted for volume %s\n", devicePath, getLabel());
268            setState(Volume::State_Mounted);
269            mCurrentlyMountedKdev = deviceNodes[i];
270            return 0;
271        }
272
273        LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
274    }
275
276    LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
277    setState(Volume::State_Idle);
278
279    return -1;
280}
281
282int Volume::unmountVol() {
283    int i, rc;
284
285    if (getState() != Volume::State_Mounted) {
286        LOGE("Volume %s unmount request when not mounted", getLabel());
287        errno = EINVAL;
288        return -1;
289    }
290
291    setState(Volume::State_Unmounting);
292    for (i = 0; i < 10; i++) {
293        rc = umount(getMountpoint());
294        if (!rc)
295            break;
296
297        if (rc && (errno == EINVAL || errno == ENOENT)) {
298            rc = 0;
299            break;
300        }
301
302        LOGW("Volume %s unmount attempt %d failed (%s)",
303             getLabel(), i + 1, strerror(errno));
304
305        if (i < 5) {
306            usleep(1000 * 250);
307        } else {
308            KillProcessesWithOpenFiles(getMountpoint(),
309                                       (i < 7 ? 0 : 1),
310                                       NULL, 0);
311            usleep(1000 * 250);
312        }
313    }
314
315    if (!rc) {
316        LOGI("Volume %s unmounted sucessfully", getLabel());
317        setState(Volume::State_Idle);
318        mCurrentlyMountedKdev = -1;
319        return 0;
320    }
321
322    LOGE("Volume %s failed to unmount (%s)\n", getLabel(), strerror(errno));
323    setState(Volume::State_Mounted);
324    return -1;
325}
326
327int Volume::initializeMbr(const char *deviceNode) {
328    int fd, rc;
329    unsigned char block[512];
330    struct dos_partition part;
331    unsigned int nr_sec;
332
333    if ((fd = open(deviceNode, O_RDWR)) < 0) {
334        LOGE("Error opening disk file (%s)", strerror(errno));
335        return -1;
336    }
337
338    if (ioctl(fd, BLKGETSIZE, &nr_sec)) {
339        LOGE("Unable to get device size (%s)", strerror(errno));
340        close(fd);
341        return -1;
342    }
343
344    memset(&part, 0, sizeof(part));
345    part.dp_flag = 0x80;
346    part.dp_typ = 0xc;
347    part.dp_start = ((1024 * 64) / 512) + 1;
348    part.dp_size = nr_sec - part.dp_start;
349
350    memset(block, 0, sizeof(block));
351    block[0x1fe] = 0x55;
352    block[0x1ff] = 0xaa;
353
354    dos_partition_enc(block + DOSPARTOFF, &part);
355
356    if (write(fd, block, sizeof(block)) < 0) {
357        LOGE("Error writing MBR (%s)", strerror(errno));
358        close(fd);
359        return -1;
360    }
361
362    if (ioctl(fd, BLKRRPART, NULL) < 0) {
363        LOGE("Error re-reading partition table (%s)", strerror(errno));
364        close(fd);
365        return -1;
366    }
367    close(fd);
368    return 0;
369}
370