blob: a7b789c16bbcc9f350a2d902fe0b90b22ad6adab [file] [log] [blame]
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/inotify.h>
#include <sys/stat.h>
#include "vold.h"
#include "inotify.h"
#include "blkdev.h"
#include "volmgr.h"
#define DEBUG_INOTIFY 0
static int handle_inotify_event(struct inotify_event *evt);
int process_inotify_event(int fd)
{
char buffer[512];
int len;
int offset = 0;
if ((len = read(fd, buffer, sizeof(buffer))) < 0) {
LOGE("Unable to read inotify event (%m)\n");
return -errno;
}
while (len >= (int) sizeof(struct inotify_event)) {
struct inotify_event *evt = (struct inotify_event *) &buffer[offset];
if (handle_inotify_event(evt) < 0)
LOGE("Error handling inotify event (%m)\n");
len -= sizeof(struct inotify_event) + evt->len;
offset += sizeof(struct inotify_event) + evt->len;
}
return 0;
}
struct blk_dev_entry {
int minor;
char *name;
struct blk_dev_entry *next;
};
int inotify_bootstrap(void)
{
DIR *d;
struct dirent *de;
if (!(d = opendir(DEVPATH))) {
LOGE("Unable to open directory '%s' (%m)\n", DEVPATH);
return -errno;
}
struct blk_dev_entry *blkdevs[255];
memset(blkdevs, 0, sizeof(blkdevs));
while((de = readdir(d))) {
char filename[255];
struct stat sbuf;
if (de->d_name[0] == '.')
continue;
sprintf(filename, "%s/%s", DEVPATH, de->d_name);
if (stat(filename, &sbuf) < 0) {
LOGE("Unable to stat '%s' (%m)\n", filename);
continue;
}
if (!S_ISBLK(sbuf.st_mode))
continue;
int major = (sbuf.st_rdev & 0xfff00) >> 8;
int minor = (sbuf.st_rdev & 0xff) | ((sbuf.st_rdev >> 12) & 0xfff00);
struct blk_dev_entry *entry;
if (!(entry = malloc(sizeof(struct blk_dev_entry)))) {
LOGE("Out of memory\n");
break;
}
entry->minor = minor;
entry->name = strdup(de->d_name);
entry->next = NULL;
if (!blkdevs[major])
blkdevs[major] = entry;
else {
struct blk_dev_entry *scan = blkdevs[major];
/*
* Insert the entry in minor number ascending order
*/
while(scan) {
if (minor < scan->minor) {
entry->next = scan;
if (scan == blkdevs[major])
blkdevs[major] = entry;
else
scan->next = entry;
break;
}
scan = scan->next;
}
if (!scan) {
scan = blkdevs[major];
while(scan->next)
scan = scan->next;
scan->next = entry;
}
}
}
closedir(d);
int i = 0;
for (i = 0; i < 255; i++) {
if (!blkdevs[i])
continue;
struct blk_dev_entry *scan = blkdevs[i];
while(scan) {
struct inotify_event *evt;
int len;
len = sizeof(struct inotify_event) + strlen(scan->name);
if (!(evt = malloc(len))) {
LOGE("Out of memory\n");
break;
}
memset(evt, 0, len);
strcpy(evt->name, scan->name);
evt->mask = IN_CREATE;
if (handle_inotify_event(evt) < 0)
LOGE("Error handling bootstrapped inotify event (%m)\n");
free(evt);
scan = scan->next;
}
}
for (i = 0; i < 255; i++) {
if (!blkdevs[i])
continue;
if (!blkdevs[i]->next) {
free(blkdevs[i]->name);
free(blkdevs[i]);
blkdevs[i] = NULL;
continue;
}
struct blk_dev_entry *scan = blkdevs[i];
while(scan) {
struct blk_dev_entry *next = scan->next->next;
free(scan->next->name);
free(scan->next);
scan->next = next;
scan = next;
}
} // for
return 0;
}
static int handle_inotify_event(struct inotify_event *evt)
{
char filename[255];
int rc;
#if DEBUG_INOTIFY
LOG_VOL("Inotify '%s' %s\n", evt->name, (evt->mask == IN_CREATE ? "created" : "deleted"));
#endif
sprintf(filename, "%s%s", DEVPATH, evt->name);
if (evt->mask == IN_CREATE) {
struct stat sbuf;
if (stat(filename, &sbuf) < 0) {
LOGE("Unable to stat '%s' (%m)\n", filename);
return -errno;
}
if (!S_ISBLK(sbuf.st_mode)) {
#if DEBUG_INOTIFY
LOG_VOL("Ignoring inotify on '%s' (not a block device)\n", evt->name);
#endif
return 0;
}
int major = (sbuf.st_rdev & 0xfff00) >> 8;
int minor = (sbuf.st_rdev & 0xff) | ((sbuf.st_rdev >> 12) & 0xfff00);
blkdev_t *blkdev = blkdev_lookup_by_devno(major, minor);
if ((rc = blkdev_handle_devicefile_created(blkdev, filename)) < 0) {
LOGE("Error handling device file '%s' creation (%s)\n", filename, strerror(rc));
return rc;
}
if (!blkdev) {
#if DEBUG_INOTIFY
LOG_VOL("No backing blkdev for '%s' available (yet) - pending volmgr dispatch\n", filename);
#endif
return 0;
}
#if DEBUG_INOTIFY
LOG_VOL("NUM_PENDING_PARTITIONS = %d\n", blkdev_get_num_pending_partitions(blkdev));
#endif
if (blkdev_get_num_pending_partitions(blkdev->disk) == 0) {
if ((rc = volmgr_consider_disk(blkdev->disk)) < 0) {
LOGE("Error from volmgr - %d\n", rc);
return rc;
}
}
} else {
blkdev_t *blkdev;
if (!(blkdev = blkdev_lookup_by_dev_fspath(filename))) {
#if DEBUG_INOTIFY
LOG_VOL("Ignoring removal of '%s' (no backend blkdev)\n", filename);
#endif
return 0;
}
if ((rc = blkdev_handle_devicefile_removed(blkdev, filename)) < 0) {
LOGE("Error handling device file '%s' removal (%s)\n", filename, strerror(rc));
return rc;
}
}
return 0;
}