/* * Copyright (C) 2007 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 <stdio.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <errno.h> #include <sys/mount.h> #include "mounts.h" struct MountedVolume { const char *device; const char *mount_point; const char *filesystem; const char *flags; }; typedef struct { MountedVolume *volumes; int volumes_allocd; int volume_count; } MountsState; static MountsState g_mounts_state = { NULL, // volumes 0, // volumes_allocd 0 // volume_count }; static inline void free_volume_internals(const MountedVolume *volume, int zero) { free((char *)volume->device); free((char *)volume->mount_point); free((char *)volume->filesystem); free((char *)volume->flags); if (zero) { memset((void *)volume, 0, sizeof(*volume)); } } #define PROC_MOUNTS_FILENAME "/proc/mounts" int scan_mounted_volumes() { char buf[2048]; const char *bufp; int fd; ssize_t nbytes; if (g_mounts_state.volumes == NULL) { const int numv = 32; MountedVolume *volumes = malloc(numv * sizeof(*volumes)); if (volumes == NULL) { errno = ENOMEM; return -1; } g_mounts_state.volumes = volumes; g_mounts_state.volumes_allocd = numv; memset(volumes, 0, numv * sizeof(*volumes)); } else { /* Free the old volume strings. */ int i; for (i = 0; i < g_mounts_state.volume_count; i++) { free_volume_internals(&g_mounts_state.volumes[i], 1); } } g_mounts_state.volume_count = 0; /* Open and read the file contents. */ fd = open(PROC_MOUNTS_FILENAME, O_RDONLY); if (fd < 0) { goto bail; } nbytes = read(fd, buf, sizeof(buf) - 1); close(fd); if (nbytes < 0) { goto bail; } buf[nbytes] = '\0'; /* Parse the contents of the file, which looks like: * * # cat /proc/mounts * rootfs / rootfs rw 0 0 * /dev/pts /dev/pts devpts rw 0 0 * /proc /proc proc rw 0 0 * /sys /sys sysfs rw 0 0 * /dev/block/mtdblock4 /system yaffs2 rw,nodev,noatime,nodiratime 0 0 * /dev/block/mtdblock5 /data yaffs2 rw,nodev,noatime,nodiratime 0 0 * /dev/block/mmcblk0p1 /sdcard vfat rw,sync,dirsync,fmask=0000,dmask=0000,codepage=cp437,iocharset=iso8859-1,utf8 0 0 * * The zeroes at the end are dummy placeholder fields to make the * output match Linux's /etc/mtab, but don't represent anything here. */ bufp = buf; while (nbytes > 0) { char device[64]; char mount_point[64]; char filesystem[64]; char flags[128]; int matches; /* %as is a gnu extension that malloc()s a string for each field. */ matches = sscanf(bufp, "%63s %63s %63s %127s", device, mount_point, filesystem, flags); if (matches == 4) { device[sizeof(device)-1] = '\0'; mount_point[sizeof(mount_point)-1] = '\0'; filesystem[sizeof(filesystem)-1] = '\0'; flags[sizeof(flags)-1] = '\0'; MountedVolume *v = &g_mounts_state.volumes[g_mounts_state.volume_count++]; v->device = strdup(device); v->mount_point = strdup(mount_point); v->filesystem = strdup(filesystem); v->flags = strdup(flags); } else { printf("matches was %d on <<%.40s>>\n", matches, bufp); } /* Eat the line. */ while (nbytes > 0 && *bufp != '\n') { bufp++; nbytes--; } if (nbytes > 0) { bufp++; nbytes--; } } return 0; bail: //TODO: free the strings we've allocated. g_mounts_state.volume_count = 0; return -1; } const MountedVolume * find_mounted_volume_by_device(const char *device) { if (g_mounts_state.volumes != NULL) { int i; for (i = 0; i < g_mounts_state.volume_count; i++) { MountedVolume *v = &g_mounts_state.volumes[i]; /* May be null if it was unmounted and we haven't rescanned. */ if (v->device != NULL) { if (strcmp(v->device, device) == 0) { return v; } } } } return NULL; } const MountedVolume * find_mounted_volume_by_mount_point(const char *mount_point) { if (g_mounts_state.volumes != NULL) { int i; for (i = 0; i < g_mounts_state.volume_count; i++) { MountedVolume *v = &g_mounts_state.volumes[i]; /* May be null if it was unmounted and we haven't rescanned. */ if (v->mount_point != NULL) { if (strcmp(v->mount_point, mount_point) == 0) { return v; } } } } return NULL; } int unmount_mounted_volume(const MountedVolume *volume) { /* Intentionally pass NULL to umount if the caller tries * to unmount a volume they already unmounted using this * function. */ int ret = umount(volume->mount_point); if (ret == 0) { free_volume_internals(volume, 1); return 0; } return ret; } int remount_read_only(const MountedVolume* volume) { return mount(volume->device, volume->mount_point, volume->filesystem, MS_NOATIME | MS_NODEV | MS_NODIRATIME | MS_RDONLY | MS_REMOUNT, 0); }