DirectVolume.cpp revision 2a5b8ce09b8836a8463ef9beaaff865c36ca5e6a
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                    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 (part_num > mDiskNumParts) {
182        mDiskNumParts = part_num;
183    }
184
185    if (major != mDiskMajor) {
186        LOGE("Partition '%s' has a different major than its disk!", devpath);
187        return;
188    }
189#ifdef PARTITION_DEBUG
190    LOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
191#endif
192    mPartMinors[part_num -1] = minor;
193
194    mPendingPartMap &= ~(1 << part_num);
195    if (!mPendingPartMap) {
196#ifdef PARTITION_DEBUG
197        LOGD("Dv:partAdd: Got all partitions - ready to rock!");
198#endif
199        if (getState() != Volume::State_Formatting) {
200            setState(Volume::State_Idle);
201        }
202    } else {
203#ifdef PARTITION_DEBUG
204        LOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
205#endif
206    }
207}
208
209void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {
210    int major = atoi(evt->findParam("MAJOR"));
211    int minor = atoi(evt->findParam("MINOR"));
212
213    if ((major != mDiskMajor) || (minor != mDiskMinor)) {
214        return;
215    }
216
217    LOGI("Volume %s disk has changed", getLabel());
218    const char *tmp = evt->findParam("NPARTS");
219    if (tmp) {
220        mDiskNumParts = atoi(tmp);
221    } else {
222        LOGW("Kernel block uevent missing 'NPARTS'");
223        mDiskNumParts = 1;
224    }
225
226    int partmask = 0;
227    int i;
228    for (i = 1; i <= mDiskNumParts; i++) {
229        partmask |= (1 << i);
230    }
231    mPendingPartMap = partmask;
232
233    if (getState() != Volume::State_Formatting) {
234        if (mDiskNumParts == 0) {
235            setState(Volume::State_Idle);
236        } else {
237            setState(Volume::State_Pending);
238        }
239    }
240}
241
242void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {
243    int major = atoi(evt->findParam("MAJOR"));
244    int minor = atoi(evt->findParam("MINOR"));
245    LOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);
246}
247
248void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {
249    int major = atoi(evt->findParam("MAJOR"));
250    int minor = atoi(evt->findParam("MINOR"));
251    char msg[255];
252
253    LOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
254    snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
255             getLabel(), getMountpoint(), major, minor);
256    mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
257                                             msg, false);
258    setState(Volume::State_NoMedia);
259}
260
261void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {
262    int major = atoi(evt->findParam("MAJOR"));
263    int minor = atoi(evt->findParam("MINOR"));
264    char msg[255];
265
266    LOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
267
268    /*
269     * The framework doesn't need to get notified of
270     * partition removal unless it's mounted. Otherwise
271     * the removal notification will be sent on the Disk
272     * itself
273     */
274    if (getState() != Volume::State_Mounted) {
275        return;
276    }
277
278    if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
279        /*
280         * Yikes, our mounted partition is going away!
281         */
282
283        snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
284                 getLabel(), getMountpoint(), major, minor);
285        mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
286                                             msg, false);
287        if (Volume::unmountVol(true)) {
288            LOGE("Failed to unmount volume on bad removal (%s)",
289                 strerror(errno));
290            // XXX: At this point we're screwed for now
291        } else {
292            LOGD("Crisis averted");
293        }
294    }
295}
296
297/*
298 * Called from base to get a list of devicenodes for mounting
299 */
300int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
301
302    if (mPartIdx == -1) {
303        // If the disk has no partitions, try the disk itself
304        if (!mDiskNumParts) {
305            devs[0] = MKDEV(mDiskMajor, mDiskMinor);
306            return 1;
307        }
308
309        int i;
310        for (i = 0; i < mDiskNumParts; i++) {
311            if (i == max)
312                break;
313            devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
314        }
315        return mDiskNumParts;
316    }
317    devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
318    return 1;
319}
320