DirectVolume.cpp revision d766090b7a72562be9e64700e13882663004650e
1be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root/*
2be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * Copyright (C) 2008 The Android Open Source Project
3be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root *
4be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * Licensed under the Apache License, Version 2.0 (the "License");
5be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * you may not use this file except in compliance with the License.
6be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * You may obtain a copy of the License at
7be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root *
8be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root *      http://www.apache.org/licenses/LICENSE-2.0
9be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root *
10be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * Unless required by applicable law or agreed to in writing, software
11be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * distributed under the License is distributed on an "AS IS" BASIS,
12be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * See the License for the specific language governing permissions and
14be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root * limitations under the License.
15be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root */
16be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
17be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <stdio.h>
18be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <stdlib.h>
19be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <string.h>
20be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <errno.h>
21be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
22be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <linux/kdev_t.h>
23be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
24be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#define LOG_TAG "DirectVolume"
25be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
26be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <cutils/log.h>
27be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include <sysutils/NetlinkEvent.h>
28be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
29be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include "DirectVolume.h"
30be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include "VolumeManager.h"
31be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root#include "ResponseCode.h"
32be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
33be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root// #define PARTITION_DEBUG
34be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
35be857d42849eaaa554d4772dbba7755f8a0f3547Kenny RootDirectVolume::DirectVolume(VolumeManager *vm, const char *label,
36be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root                           const char *mount_point, int partIdx) :
37be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root              Volume(vm, label, mount_point) {
38be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    mPartIdx = partIdx;
39be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
40be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    mPaths = new PathCollection();
41be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    for (int i = 0; i < MAX_PARTITIONS; i++)
42be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root        mPartMinors[i] = -1;
43be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    mPendingPartMap = 0;
44be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    mDiskMajor = -1;
45be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    mDiskMinor = -1;
46be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    mDiskNumParts = 0;
47be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
48be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    setState(Volume::State_NoMedia);
49be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root}
50be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
51be857d42849eaaa554d4772dbba7755f8a0f3547Kenny RootDirectVolume::~DirectVolume() {
52be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root    PathCollection::iterator it;
53be857d42849eaaa554d4772dbba7755f8a0f3547Kenny Root
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
270    SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
271
272    /*
273     * The framework doesn't need to get notified of
274     * partition removal unless it's mounted. Otherwise
275     * the removal notification will be sent on the Disk
276     * itself
277     */
278    if (getState() != Volume::State_Mounted) {
279        return;
280    }
281
282    if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
283        /*
284         * Yikes, our mounted partition is going away!
285         */
286
287        snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
288                 getLabel(), getMountpoint(), major, minor);
289        mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
290                                             msg, false);
291
292	if (mVm->cleanupAsec(this, true)) {
293            SLOGE("Failed to cleanup ASEC - unmount will probably fail!");
294        }
295
296        if (Volume::unmountVol(true)) {
297            SLOGE("Failed to unmount volume on bad removal (%s)",
298                 strerror(errno));
299            // XXX: At this point we're screwed for now
300        } else {
301            SLOGD("Crisis averted");
302        }
303    }
304}
305
306/*
307 * Called from base to get a list of devicenodes for mounting
308 */
309int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
310
311    if (mPartIdx == -1) {
312        // If the disk has no partitions, try the disk itself
313        if (!mDiskNumParts) {
314            devs[0] = MKDEV(mDiskMajor, mDiskMinor);
315            return 1;
316        }
317
318        int i;
319        for (i = 0; i < mDiskNumParts; i++) {
320            if (i == max)
321                break;
322            devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
323        }
324        return mDiskNumParts;
325    }
326    devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
327    return 1;
328}
329