DirectVolume.cpp revision 7b8f2db4b07deaaa2f6f5ffbee0386a394032b08
1706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings/*
2706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * Copyright (C) 2008 The Android Open Source Project
3706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings *
4706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * Licensed under the Apache License, Version 2.0 (the "License");
5706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * you may not use this file except in compliance with the License.
6706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * You may obtain a copy of the License at
7706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings *
8706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings *      http://www.apache.org/licenses/LICENSE-2.0
9706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings *
10706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * Unless required by applicable law or agreed to in writing, software
11706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * distributed under the License is distributed on an "AS IS" BASIS,
12706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * See the License for the specific language governing permissions and
14706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings * limitations under the License.
15706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings */
16706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings
17706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings#include <stdio.h>
18706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings#include <stdlib.h>
19706e8ba26bf0de19ad5f736516dae40c4c88c2d7rich cannings#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                    LOGE("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                    LOGW("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        LOGW("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        LOGD("Dv::diskIns - No partitions - good to go son!");
150#endif
151        setState(Volume::State_Idle);
152    } else {
153#ifdef PARTITION_DEBUG
154        LOGD("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        LOGW("Kernel block uevent missing 'PARTN'");
178        part_num = 1;
179    }
180
181    if (major != mDiskMajor) {
182        LOGE("Partition '%s' has a different major than its disk!", devpath);
183        return;
184    }
185#ifdef PARTITION_DEBUG
186    LOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
187#endif
188    mPartMinors[part_num -1] = minor;
189
190    mPendingPartMap &= ~(1 << part_num);
191    if (!mPendingPartMap) {
192#ifdef PARTITION_DEBUG
193        LOGD("Dv:partAdd: Got all partitions - ready to rock!");
194#endif
195        setState(Volume::State_Idle);
196    } else {
197#ifdef PARTITION_DEBUG
198        LOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
199#endif
200    }
201}
202
203void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {
204    int major = atoi(evt->findParam("MAJOR"));
205    int minor = atoi(evt->findParam("MINOR"));
206
207    if ((major != mDiskMajor) || (minor != mDiskMinor)) {
208        return;
209    }
210
211    LOGI("Volume %s disk has changed", getLabel());
212    const char *tmp = evt->findParam("NPARTS");
213    if (tmp) {
214        mDiskNumParts = atoi(tmp);
215    } else {
216        LOGW("Kernel block uevent missing 'NPARTS'");
217        mDiskNumParts = 1;
218    }
219
220    int partmask = 0;
221    int i;
222    for (i = 1; i <= mDiskNumParts; i++) {
223        partmask |= (1 << i);
224    }
225    mPendingPartMap = partmask;
226
227    if (mDiskNumParts == 0) {
228        setState(Volume::State_Idle);
229    } else {
230        setState(Volume::State_Pending);
231    }
232
233}
234
235void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {
236}
237
238void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {
239    int major = atoi(evt->findParam("MAJOR"));
240    int minor = atoi(evt->findParam("MINOR"));
241    char msg[255];
242
243    LOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
244    snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
245             getLabel(), getMountpoint(), major, minor);
246    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
247                                             msg, false);
248    setState(Volume::State_NoMedia);
249}
250
251void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {
252    int major = atoi(evt->findParam("MAJOR"));
253    int minor = atoi(evt->findParam("MINOR"));
254    char msg[255];
255
256    LOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
257
258    /*
259     * The framework doesn't need to get notified of
260     * partition removal unless it's mounted. Otherwise
261     * the removal notification will be sent on the Disk
262     * itself
263     */
264    if (getState() != Volume::State_Mounted) {
265        return;
266    }
267
268    if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
269        /*
270         * Yikes, our mounted partition is going away!
271         */
272
273        snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
274                 getLabel(), getMountpoint(), major, minor);
275        mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
276                                             msg, false);
277        if (Volume::unmountVol()) {
278            LOGE("Failed to unmount volume on bad removal (%s)",
279                 strerror(errno));
280            // XXX: At this point we're screwed for now
281        } else {
282            LOGD("Crisis averted");
283        }
284    }
285}
286
287/*
288 * Called from base to get a list of devicenodes for mounting
289 */
290int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
291
292    if (mPartIdx == -1) {
293        // If the disk has no partitions, try the disk itself
294        if (!mDiskNumParts) {
295            devs[0] = MKDEV(mDiskMajor, mDiskMinor);
296            return 1;
297        }
298
299        int i;
300        for (i = 0; i < mDiskNumParts; i++) {
301            if (i == max)
302                break;
303            devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
304        }
305        return mDiskNumParts;
306    }
307    devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
308    return 1;
309}
310