DirectVolume.cpp revision 75a3e1a95af0a2790de1b12aeca0008bfdc61649
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 <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <errno.h>
21
22#include <linux/kdev_t.h>
23
24#define LOG_TAG "DirectVolume"
25
26#include <cutils/log.h>
27#include <sysutils/NetlinkEvent.h>
28
29#include "DirectVolume.h"
30#include "VolumeManager.h"
31#include "ResponseCode.h"
32
33// #define PARTITION_DEBUG
34
35DirectVolume::DirectVolume(VolumeManager *vm, const char *label,
36                           const char *mount_point, int partIdx) :
37              Volume(vm, label, mount_point) {
38    mPartIdx = partIdx;
39
40    mPaths = new PathCollection();
41    for (int i = 0; i < MAX_PARTITIONS; i++)
42        mPartMinors[i] = -1;
43    mPendingPartMap = 0;
44    mDiskMajor = -1;
45    mDiskMinor = -1;
46    mDiskNumParts = 0;
47
48    setState(Volume::State_NoMedia);
49}
50
51DirectVolume::~DirectVolume() {
52    PathCollection::iterator it;
53
54    for (it = mPaths->begin(); it != mPaths->end(); ++it)
55        free(*it);
56    delete mPaths;
57}
58
59int DirectVolume::addPath(const char *path) {
60    mPaths->push_back(strdup(path));
61    return 0;
62}
63
64dev_t DirectVolume::getDiskDevice() {
65    return MKDEV(mDiskMajor, mDiskMinor);
66}
67
68void DirectVolume::handleVolumeShared() {
69    setState(Volume::State_Shared);
70}
71
72void DirectVolume::handleVolumeUnshared() {
73    setState(Volume::State_Idle);
74}
75
76int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
77    const char *dp = evt->findParam("DEVPATH");
78
79    PathCollection::iterator  it;
80    for (it = mPaths->begin(); it != mPaths->end(); ++it) {
81        if (!strncmp(dp, *it, strlen(*it))) {
82            /* We can handle this disk */
83            int action = evt->getAction();
84            const char *devtype = evt->findParam("DEVTYPE");
85
86            if (action == NetlinkEvent::NlActionAdd) {
87                int major = atoi(evt->findParam("MAJOR"));
88                int minor = atoi(evt->findParam("MINOR"));
89                char nodepath[255];
90
91                snprintf(nodepath,
92                         sizeof(nodepath), "/dev/block/vold/%d:%d",
93                         major, minor);
94                if (createDeviceNode(nodepath, major, minor)) {
95                    SLOGE("Error making device node '%s' (%s)", nodepath,
96                                                               strerror(errno));
97                }
98                if (!strcmp(devtype, "disk")) {
99                    handleDiskAdded(dp, evt);
100                } else {
101                    handlePartitionAdded(dp, evt);
102                }
103            } else if (action == NetlinkEvent::NlActionRemove) {
104                if (!strcmp(devtype, "disk")) {
105                    handleDiskRemoved(dp, evt);
106                } else {
107                    handlePartitionRemoved(dp, evt);
108                }
109            } else if (action == NetlinkEvent::NlActionChange) {
110                if (!strcmp(devtype, "disk")) {
111                    handleDiskChanged(dp, evt);
112                } else {
113                    handlePartitionChanged(dp, evt);
114                }
115            } else {
116                    SLOGW("Ignoring non add/remove/change event");
117            }
118
119            return 0;
120        }
121    }
122    errno = ENODEV;
123    return -1;
124}
125
126void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
127    mDiskMajor = atoi(evt->findParam("MAJOR"));
128    mDiskMinor = atoi(evt->findParam("MINOR"));
129
130    const char *tmp = evt->findParam("NPARTS");
131    if (tmp) {
132        mDiskNumParts = atoi(tmp);
133    } else {
134        SLOGW("Kernel block uevent missing 'NPARTS'");
135        mDiskNumParts = 1;
136    }
137
138    char msg[255];
139
140    int partmask = 0;
141    int i;
142    for (i = 1; i <= mDiskNumParts; i++) {
143        partmask |= (1 << i);
144    }
145    mPendingPartMap = partmask;
146
147    if (mDiskNumParts == 0) {
148#ifdef PARTITION_DEBUG
149        SLOGD("Dv::diskIns - No partitions - good to go son!");
150#endif
151        setState(Volume::State_Idle);
152    } else {
153#ifdef PARTITION_DEBUG
154        SLOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",
155             mDiskNumParts, mPendingPartMap);
156#endif
157        setState(Volume::State_Pending);
158    }
159
160    snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",
161             getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);
162    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
163                                             msg, false);
164}
165
166void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {
167    int major = atoi(evt->findParam("MAJOR"));
168    int minor = atoi(evt->findParam("MINOR"));
169
170    int part_num;
171
172    const char *tmp = evt->findParam("PARTN");
173
174    if (tmp) {
175        part_num = atoi(tmp);
176    } else {
177        SLOGW("Kernel block uevent missing 'PARTN'");
178        part_num = 1;
179    }
180
181    if (part_num > mDiskNumParts) {
182        mDiskNumParts = part_num;
183    }
184
185    if (major != mDiskMajor) {
186        SLOGE("Partition '%s' has a different major than its disk!", devpath);
187        return;
188    }
189#ifdef PARTITION_DEBUG
190    SLOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
191#endif
192    if (part_num >= MAX_PARTITIONS) {
193        SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS-1);
194    } else {
195        mPartMinors[part_num -1] = minor;
196    }
197    mPendingPartMap &= ~(1 << part_num);
198
199    if (!mPendingPartMap) {
200#ifdef PARTITION_DEBUG
201        SLOGD("Dv:partAdd: Got all partitions - ready to rock!");
202#endif
203        if (getState() != Volume::State_Formatting) {
204            setState(Volume::State_Idle);
205        }
206    } else {
207#ifdef PARTITION_DEBUG
208        SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
209#endif
210    }
211}
212
213void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {
214    int major = atoi(evt->findParam("MAJOR"));
215    int minor = atoi(evt->findParam("MINOR"));
216
217    if ((major != mDiskMajor) || (minor != mDiskMinor)) {
218        return;
219    }
220
221    SLOGI("Volume %s disk has changed", getLabel());
222    const char *tmp = evt->findParam("NPARTS");
223    if (tmp) {
224        mDiskNumParts = atoi(tmp);
225    } else {
226        SLOGW("Kernel block uevent missing 'NPARTS'");
227        mDiskNumParts = 1;
228    }
229
230    int partmask = 0;
231    int i;
232    for (i = 1; i <= mDiskNumParts; i++) {
233        partmask |= (1 << i);
234    }
235    mPendingPartMap = partmask;
236
237    if (getState() != Volume::State_Formatting) {
238        if (mDiskNumParts == 0) {
239            setState(Volume::State_Idle);
240        } else {
241            setState(Volume::State_Pending);
242        }
243    }
244}
245
246void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {
247    int major = atoi(evt->findParam("MAJOR"));
248    int minor = atoi(evt->findParam("MINOR"));
249    SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);
250}
251
252void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {
253    int major = atoi(evt->findParam("MAJOR"));
254    int minor = atoi(evt->findParam("MINOR"));
255    char msg[255];
256
257    SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
258    snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
259             getLabel(), getMountpoint(), major, minor);
260    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
261                                             msg, false);
262    setState(Volume::State_NoMedia);
263}
264
265void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {
266    int major = atoi(evt->findParam("MAJOR"));
267    int minor = atoi(evt->findParam("MINOR"));
268    char msg[255];
269    int state;
270
271    SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
272
273    /*
274     * The framework doesn't need to get notified of
275     * partition removal unless it's mounted. Otherwise
276     * the removal notification will be sent on the Disk
277     * itself
278     */
279    state = getState();
280    if (state != Volume::State_Mounted && state != Volume::State_Shared) {
281        return;
282    }
283
284    if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
285        /*
286         * Yikes, our mounted partition is going away!
287         */
288
289        snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
290                 getLabel(), getMountpoint(), major, minor);
291        mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
292                                             msg, false);
293
294	if (mVm->cleanupAsec(this, true)) {
295            SLOGE("Failed to cleanup ASEC - unmount will probably fail!");
296        }
297
298        if (Volume::unmountVol(true)) {
299            SLOGE("Failed to unmount volume on bad removal (%s)",
300                 strerror(errno));
301            // XXX: At this point we're screwed for now
302        } else {
303            SLOGD("Crisis averted");
304        }
305    } else if (state == Volume::State_Shared) {
306        /* removed during mass storage */
307        snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",
308                 getLabel(), major, minor);
309        mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
310                                             msg, false);
311
312        if (mVm->unshareVolume(getLabel(), "ums")) {
313            SLOGE("Failed to unshare volume on bad removal (%s)",
314                strerror(errno));
315        } else {
316            SLOGD("Crisis averted");
317        }
318    }
319}
320
321/*
322 * Called from base to get a list of devicenodes for mounting
323 */
324int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
325
326    if (mPartIdx == -1) {
327        // If the disk has no partitions, try the disk itself
328        if (!mDiskNumParts) {
329            devs[0] = MKDEV(mDiskMajor, mDiskMinor);
330            return 1;
331        }
332
333        int i;
334        for (i = 0; i < mDiskNumParts; i++) {
335            if (i == max)
336                break;
337            devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
338        }
339        return mDiskNumParts;
340    }
341    devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
342    return 1;
343}
344