• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 #include "cryptfs.h"
33 
34 // #define PARTITION_DEBUG
35 
DirectVolume(VolumeManager * vm,const fstab_rec * rec,int flags)36 DirectVolume::DirectVolume(VolumeManager *vm, const fstab_rec* rec, int flags) :
37         Volume(vm, rec, flags) {
38     mPaths = new PathCollection();
39     for (int i = 0; i < MAX_PARTITIONS; i++)
40         mPartMinors[i] = -1;
41     mPendingPartMap = 0;
42     mDiskMajor = -1;
43     mDiskMinor = -1;
44     mDiskNumParts = 0;
45 
46     if (strcmp(rec->mount_point, "auto") != 0) {
47         ALOGE("Vold managed volumes must have auto mount point; ignoring %s",
48               rec->mount_point);
49     }
50 
51     char mount[PATH_MAX];
52 
53     snprintf(mount, PATH_MAX, "%s/%s", Volume::MEDIA_DIR, rec->label);
54     mMountpoint = strdup(mount);
55     snprintf(mount, PATH_MAX, "%s/%s", Volume::FUSE_DIR, rec->label);
56     mFuseMountpoint = strdup(mount);
57 
58     setState(Volume::State_NoMedia);
59 }
60 
~DirectVolume()61 DirectVolume::~DirectVolume() {
62     PathCollection::iterator it;
63 
64     for (it = mPaths->begin(); it != mPaths->end(); ++it)
65         free(*it);
66     delete mPaths;
67 }
68 
addPath(const char * path)69 int DirectVolume::addPath(const char *path) {
70     mPaths->push_back(strdup(path));
71     return 0;
72 }
73 
getDiskDevice()74 dev_t DirectVolume::getDiskDevice() {
75     return MKDEV(mDiskMajor, mDiskMinor);
76 }
77 
getShareDevice()78 dev_t DirectVolume::getShareDevice() {
79     if (mPartIdx != -1) {
80         return MKDEV(mDiskMajor, mPartIdx);
81     } else {
82         return MKDEV(mDiskMajor, mDiskMinor);
83     }
84 }
85 
handleVolumeShared()86 void DirectVolume::handleVolumeShared() {
87     setState(Volume::State_Shared);
88 }
89 
handleVolumeUnshared()90 void DirectVolume::handleVolumeUnshared() {
91     setState(Volume::State_Idle);
92 }
93 
handleBlockEvent(NetlinkEvent * evt)94 int DirectVolume::handleBlockEvent(NetlinkEvent *evt) {
95     const char *dp = evt->findParam("DEVPATH");
96 
97     PathCollection::iterator  it;
98     for (it = mPaths->begin(); it != mPaths->end(); ++it) {
99         if (!strncmp(dp, *it, strlen(*it))) {
100             /* We can handle this disk */
101             int action = evt->getAction();
102             const char *devtype = evt->findParam("DEVTYPE");
103 
104             if (action == NetlinkEvent::NlActionAdd) {
105                 int major = atoi(evt->findParam("MAJOR"));
106                 int minor = atoi(evt->findParam("MINOR"));
107                 char nodepath[255];
108 
109                 snprintf(nodepath,
110                          sizeof(nodepath), "/dev/block/vold/%d:%d",
111                          major, minor);
112                 if (createDeviceNode(nodepath, major, minor)) {
113                     SLOGE("Error making device node '%s' (%s)", nodepath,
114                                                                strerror(errno));
115                 }
116                 if (!strcmp(devtype, "disk")) {
117                     handleDiskAdded(dp, evt);
118                 } else {
119                     handlePartitionAdded(dp, evt);
120                 }
121                 /* Send notification iff disk is ready (ie all partitions found) */
122                 if (getState() == Volume::State_Idle) {
123                     char msg[255];
124 
125                     snprintf(msg, sizeof(msg),
126                              "Volume %s %s disk inserted (%d:%d)", getLabel(),
127                              getFuseMountpoint(), mDiskMajor, mDiskMinor);
128                     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,
129                                                          msg, false);
130                 }
131             } else if (action == NetlinkEvent::NlActionRemove) {
132                 if (!strcmp(devtype, "disk")) {
133                     handleDiskRemoved(dp, evt);
134                 } else {
135                     handlePartitionRemoved(dp, evt);
136                 }
137             } else if (action == NetlinkEvent::NlActionChange) {
138                 if (!strcmp(devtype, "disk")) {
139                     handleDiskChanged(dp, evt);
140                 } else {
141                     handlePartitionChanged(dp, evt);
142                 }
143             } else {
144                     SLOGW("Ignoring non add/remove/change event");
145             }
146 
147             return 0;
148         }
149     }
150     errno = ENODEV;
151     return -1;
152 }
153 
handleDiskAdded(const char * devpath,NetlinkEvent * evt)154 void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {
155     mDiskMajor = atoi(evt->findParam("MAJOR"));
156     mDiskMinor = atoi(evt->findParam("MINOR"));
157 
158     const char *tmp = evt->findParam("NPARTS");
159     if (tmp) {
160         mDiskNumParts = atoi(tmp);
161     } else {
162         SLOGW("Kernel block uevent missing 'NPARTS'");
163         mDiskNumParts = 1;
164     }
165 
166     int partmask = 0;
167     int i;
168     for (i = 1; i <= mDiskNumParts; i++) {
169         partmask |= (1 << i);
170     }
171     mPendingPartMap = partmask;
172 
173     if (mDiskNumParts == 0) {
174 #ifdef PARTITION_DEBUG
175         SLOGD("Dv::diskIns - No partitions - good to go son!");
176 #endif
177         setState(Volume::State_Idle);
178     } else {
179 #ifdef PARTITION_DEBUG
180         SLOGD("Dv::diskIns - waiting for %d partitions (mask 0x%x)",
181              mDiskNumParts, mPendingPartMap);
182 #endif
183         setState(Volume::State_Pending);
184     }
185 }
186 
handlePartitionAdded(const char * devpath,NetlinkEvent * evt)187 void DirectVolume::handlePartitionAdded(const char *devpath, NetlinkEvent *evt) {
188     int major = atoi(evt->findParam("MAJOR"));
189     int minor = atoi(evt->findParam("MINOR"));
190 
191     int part_num;
192 
193     const char *tmp = evt->findParam("PARTN");
194 
195     if (tmp) {
196         part_num = atoi(tmp);
197     } else {
198         SLOGW("Kernel block uevent missing 'PARTN'");
199         part_num = 1;
200     }
201 
202     if (part_num > MAX_PARTITIONS || part_num < 1) {
203         SLOGE("Invalid 'PARTN' value");
204         return;
205     }
206 
207     if (part_num > mDiskNumParts) {
208         mDiskNumParts = part_num;
209     }
210 
211     if (major != mDiskMajor) {
212         SLOGE("Partition '%s' has a different major than its disk!", devpath);
213         return;
214     }
215 #ifdef PARTITION_DEBUG
216     SLOGD("Dv:partAdd: part_num = %d, minor = %d\n", part_num, minor);
217 #endif
218     if (part_num >= MAX_PARTITIONS) {
219         SLOGE("Dv:partAdd: ignoring part_num = %d (max: %d)\n", part_num, MAX_PARTITIONS-1);
220     } else {
221         mPartMinors[part_num -1] = minor;
222     }
223     mPendingPartMap &= ~(1 << part_num);
224 
225     if (!mPendingPartMap) {
226 #ifdef PARTITION_DEBUG
227         SLOGD("Dv:partAdd: Got all partitions - ready to rock!");
228 #endif
229         if (getState() != Volume::State_Formatting) {
230             setState(Volume::State_Idle);
231             if (mRetryMount == true) {
232                 mRetryMount = false;
233                 mountVol();
234             }
235         }
236     } else {
237 #ifdef PARTITION_DEBUG
238         SLOGD("Dv:partAdd: pending mask now = 0x%x", mPendingPartMap);
239 #endif
240     }
241 }
242 
handleDiskChanged(const char * devpath,NetlinkEvent * evt)243 void DirectVolume::handleDiskChanged(const char *devpath, NetlinkEvent *evt) {
244     int major = atoi(evt->findParam("MAJOR"));
245     int minor = atoi(evt->findParam("MINOR"));
246 
247     if ((major != mDiskMajor) || (minor != mDiskMinor)) {
248         return;
249     }
250 
251     SLOGI("Volume %s disk has changed", getLabel());
252     const char *tmp = evt->findParam("NPARTS");
253     if (tmp) {
254         mDiskNumParts = atoi(tmp);
255     } else {
256         SLOGW("Kernel block uevent missing 'NPARTS'");
257         mDiskNumParts = 1;
258     }
259 
260     int partmask = 0;
261     int i;
262     for (i = 1; i <= mDiskNumParts; i++) {
263         partmask |= (1 << i);
264     }
265     mPendingPartMap = partmask;
266 
267     if (getState() != Volume::State_Formatting) {
268         if (mDiskNumParts == 0) {
269             setState(Volume::State_Idle);
270         } else {
271             setState(Volume::State_Pending);
272         }
273     }
274 }
275 
handlePartitionChanged(const char * devpath,NetlinkEvent * evt)276 void DirectVolume::handlePartitionChanged(const char *devpath, NetlinkEvent *evt) {
277     int major = atoi(evt->findParam("MAJOR"));
278     int minor = atoi(evt->findParam("MINOR"));
279     SLOGD("Volume %s %s partition %d:%d changed\n", getLabel(), getMountpoint(), major, minor);
280 }
281 
handleDiskRemoved(const char * devpath,NetlinkEvent * evt)282 void DirectVolume::handleDiskRemoved(const char *devpath, NetlinkEvent *evt) {
283     int major = atoi(evt->findParam("MAJOR"));
284     int minor = atoi(evt->findParam("MINOR"));
285     char msg[255];
286     bool enabled;
287 
288     if (mVm->shareEnabled(getLabel(), "ums", &enabled) == 0 && enabled) {
289         mVm->unshareVolume(getLabel(), "ums");
290     }
291 
292     SLOGD("Volume %s %s disk %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
293     snprintf(msg, sizeof(msg), "Volume %s %s disk removed (%d:%d)",
294              getLabel(), getFuseMountpoint(), major, minor);
295     mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskRemoved,
296                                              msg, false);
297     setState(Volume::State_NoMedia);
298 }
299 
handlePartitionRemoved(const char * devpath,NetlinkEvent * evt)300 void DirectVolume::handlePartitionRemoved(const char *devpath, NetlinkEvent *evt) {
301     int major = atoi(evt->findParam("MAJOR"));
302     int minor = atoi(evt->findParam("MINOR"));
303     char msg[255];
304     int state;
305 
306     SLOGD("Volume %s %s partition %d:%d removed\n", getLabel(), getMountpoint(), major, minor);
307 
308     /*
309      * The framework doesn't need to get notified of
310      * partition removal unless it's mounted. Otherwise
311      * the removal notification will be sent on the Disk
312      * itself
313      */
314     state = getState();
315     if (state != Volume::State_Mounted && state != Volume::State_Shared) {
316         return;
317     }
318 
319     if ((dev_t) MKDEV(major, minor) == mCurrentlyMountedKdev) {
320         /*
321          * Yikes, our mounted partition is going away!
322          */
323 
324         bool providesAsec = (getFlags() & VOL_PROVIDES_ASEC) != 0;
325         if (providesAsec && mVm->cleanupAsec(this, true)) {
326             SLOGE("Failed to cleanup ASEC - unmount will probably fail!");
327         }
328 
329         snprintf(msg, sizeof(msg), "Volume %s %s bad removal (%d:%d)",
330                  getLabel(), getFuseMountpoint(), major, minor);
331         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
332                                              msg, false);
333 
334         if (Volume::unmountVol(true, false)) {
335             SLOGE("Failed to unmount volume on bad removal (%s)",
336                  strerror(errno));
337             // XXX: At this point we're screwed for now
338         } else {
339             SLOGD("Crisis averted");
340         }
341     } else if (state == Volume::State_Shared) {
342         /* removed during mass storage */
343         snprintf(msg, sizeof(msg), "Volume %s bad removal (%d:%d)",
344                  getLabel(), major, minor);
345         mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeBadRemoval,
346                                              msg, false);
347 
348         if (mVm->unshareVolume(getLabel(), "ums")) {
349             SLOGE("Failed to unshare volume on bad removal (%s)",
350                 strerror(errno));
351         } else {
352             SLOGD("Crisis averted");
353         }
354     }
355 }
356 
357 /*
358  * Called from base to get a list of devicenodes for mounting
359  */
getDeviceNodes(dev_t * devs,int max)360 int DirectVolume::getDeviceNodes(dev_t *devs, int max) {
361 
362     if (mPartIdx == -1) {
363         // If the disk has no partitions, try the disk itself
364         if (!mDiskNumParts) {
365             devs[0] = MKDEV(mDiskMajor, mDiskMinor);
366             return 1;
367         }
368 
369         int i;
370         for (i = 0; i < mDiskNumParts; i++) {
371             if (i == max)
372                 break;
373             devs[i] = MKDEV(mDiskMajor, mPartMinors[i]);
374         }
375         return mDiskNumParts;
376     }
377     devs[0] = MKDEV(mDiskMajor, mPartMinors[mPartIdx -1]);
378     return 1;
379 }
380 
381 /*
382  * Called from base to update device info,
383  * e.g. When setting up an dm-crypt mapping for the sd card.
384  */
updateDeviceInfo(char * new_path,int new_major,int new_minor)385 int DirectVolume::updateDeviceInfo(char *new_path, int new_major, int new_minor)
386 {
387     PathCollection::iterator it;
388 
389     if (mPartIdx == -1) {
390         SLOGE("Can only change device info on a partition\n");
391         return -1;
392     }
393 
394     /*
395      * This is to change the sysfs path associated with a partition, in particular,
396      * for an internal SD card partition that is encrypted.  Thus, the list is
397      * expected to be only 1 entry long.  Check that and bail if not.
398      */
399     if (mPaths->size() != 1) {
400         SLOGE("Cannot change path if there are more than one for a volume\n");
401         return -1;
402     }
403 
404     it = mPaths->begin();
405     free(*it); /* Free the string storage */
406     mPaths->erase(it); /* Remove it from the list */
407     addPath(new_path); /* Put the new path on the list */
408 
409     /* Save away original info so we can restore it when doing factory reset.
410      * Then, when doing the format, it will format the original device in the
411      * clear, otherwise it just formats the encrypted device which is not
412      * readable when the device boots unencrypted after the reset.
413      */
414     mOrigDiskMajor = mDiskMajor;
415     mOrigDiskMinor = mDiskMinor;
416     mOrigPartIdx = mPartIdx;
417     memcpy(mOrigPartMinors, mPartMinors, sizeof(mPartMinors));
418 
419     mDiskMajor = new_major;
420     mDiskMinor = new_minor;
421     /* Ugh, virual block devices don't use minor 0 for whole disk and minor > 0 for
422      * partition number.  They don't have partitions, they are just virtual block
423      * devices, and minor number 0 is the first dm-crypt device.  Luckily the first
424      * dm-crypt device is for the userdata partition, which gets minor number 0, and
425      * it is not managed by vold.  So the next device is minor number one, which we
426      * will call partition one.
427      */
428     mPartIdx = new_minor;
429     mPartMinors[new_minor-1] = new_minor;
430 
431     mIsDecrypted = 1;
432 
433     return 0;
434 }
435 
436 /*
437  * Called from base to revert device info to the way it was before a
438  * crypto mapping was created for it.
439  */
revertDeviceInfo(void)440 void DirectVolume::revertDeviceInfo(void)
441 {
442     if (mIsDecrypted) {
443         mDiskMajor = mOrigDiskMajor;
444         mDiskMinor = mOrigDiskMinor;
445         mPartIdx = mOrigPartIdx;
446         memcpy(mPartMinors, mOrigPartMinors, sizeof(mPartMinors));
447 
448         mIsDecrypted = 0;
449     }
450 
451     return;
452 }
453 
454 /*
455  * Called from base to give cryptfs all the info it needs to encrypt eligible volumes
456  */
getVolInfo(struct volume_info * v)457 int DirectVolume::getVolInfo(struct volume_info *v)
458 {
459     strcpy(v->label, mLabel);
460     strcpy(v->mnt_point, mMountpoint);
461     v->flags = getFlags();
462     /* Other fields of struct volume_info are filled in by the caller or cryptfs.c */
463 
464     return 0;
465 }
466