223 lines
6.0 KiB
C
223 lines
6.0 KiB
C
|
/*
|
||
|
* 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);
|
||
|
}
|