Volume.cpp revision 8c940ef7dbd423cadc92982b44a65ed1014389e2
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);
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("Formatting volume %s (%s)", 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    if (Fat::format(devicePath)) {
170        LOGE("Failed to format (%s)", strerror(errno));
171        goto err;
172    }
173
174    return 0;
175err:
176    return -1;
177}
178
179bool Volume::isMountpointMounted(const char *path) {
180    char device[256];
181    char mount_path[256];
182    char rest[256];
183    FILE *fp;
184    char line[1024];
185
186    if (!(fp = fopen("/proc/mounts", "r"))) {
187        LOGE("Error opening /proc/mounts (%s)", strerror(errno));
188        return false;
189    }
190
191    while(fgets(line, sizeof(line), fp)) {
192        line[strlen(line)-1] = '\0';
193        sscanf(line, "%255s %255s %255s\n", device, mount_path, rest);
194        if (!strcmp(mount_path, path)) {
195            fclose(fp);
196            return true;
197        }
198
199    }
200
201    fclose(fp);
202    return false;
203}
204
205int Volume::mountVol() {
206    dev_t deviceNodes[4];
207    int n, i, rc = 0;
208    char errmsg[255];
209
210    if (getState() == Volume::State_NoMedia) {
211        snprintf(errmsg, sizeof(errmsg),
212                 "Volume %s %s mount failed - no media",
213                 getLabel(), getMountpoint());
214        mVm->getBroadcaster()->sendBroadcast(
215                                         ResponseCode::VolumeMountFailedNoMedia,
216                                         errmsg, false);
217        errno = ENODEV;
218        return -1;
219    } else if (getState() != Volume::State_Idle) {
220        errno = EBUSY;
221        return -1;
222    }
223
224    if (isMountpointMounted(getMountpoint())) {
225        LOGW("Volume is idle but appears to be mounted - fixing");
226        setState(Volume::State_Mounted);
227        // mCurrentlyMountedKdev = XXX
228        return 0;
229    }
230
231    n = getDeviceNodes((dev_t *) &deviceNodes, 4);
232    if (!n) {
233        LOGE("Failed to get device nodes (%s)\n", strerror(errno));
234        return -1;
235    }
236
237    for (i = 0; i < n; i++) {
238        char devicePath[255];
239
240        sprintf(devicePath, "/dev/block/vold/%d:%d", MAJOR(deviceNodes[i]),
241                MINOR(deviceNodes[i]));
242
243        LOGI("%s being considered for volume %s\n", devicePath, getLabel());
244
245        errno = 0;
246        setState(Volume::State_Checking);
247
248        if ((rc = Fat::check(devicePath))) {
249            if (errno == ENODATA) {
250                LOGW("%s does not contain a FAT filesystem\n", devicePath);
251                continue;
252            }
253            errno = EIO;
254            /* Badness - abort the mount */
255            LOGE("%s failed FS checks (%s)", devicePath, strerror(errno));
256            setState(Volume::State_Idle);
257            return -1;
258        }
259
260        errno = 0;
261        if (!(rc = Fat::doMount(devicePath, getMountpoint(), false, false,
262                                1000, 1015, 0702, true))) {
263            LOGI("%s sucessfully mounted for volume %s\n", devicePath, getLabel());
264            setState(Volume::State_Mounted);
265            mCurrentlyMountedKdev = deviceNodes[i];
266            return 0;
267        }
268
269        LOGW("%s failed to mount via VFAT (%s)\n", devicePath, strerror(errno));
270    }
271
272    LOGE("Volume %s found no suitable devices for mounting :(\n", getLabel());
273    setState(Volume::State_Idle);
274
275    return -1;
276}
277
278int Volume::unmountVol() {
279    int i, rc;
280
281    if (getState() != Volume::State_Mounted) {
282        LOGE("Volume %s unmount request when not mounted", getLabel());
283        errno = EINVAL;
284        return -1;
285    }
286
287    setState(Volume::State_Unmounting);
288    usleep(1000 * 200); // Give the framework some time to react
289    for (i = 1; i <= 10; i++) {
290        rc = umount(getMountpoint());
291        if (!rc)
292            break;
293
294        if (rc && (errno == EINVAL || errno == ENOENT)) {
295            rc = 0;
296            break;
297        }
298
299        LOGW("Volume %s unmount attempt %d failed (%s)",
300             getLabel(), i, strerror(errno));
301
302        int action;
303
304        if (i > 8) {
305            action = 2; // SIGKILL
306        } else if (i > 7) {
307            action = 1; // SIGHUP
308        } else
309            action = 0; // just complain
310
311        KillProcessesWithOpenFiles(getMountpoint(), action);
312        usleep(1000*250);
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