iBoot/lib/fs/fs.c

424 lines
8.7 KiB
C

/*
* Copyright (C) 2010 Apple Inc. All rights reserved.
* Copyright (C) 2006 Apple Computer, Inc. All rights reserved.
*
* This document is the property of Apple Inc.
* It is considered confidential and proprietary.
*
* This document may not be reproduced or transmitted in any form,
* in whole or in part, without the express written permission of
* Apple Inc.
*/
#include <debug.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/security.h>
#include <lib/fs.h>
#include <list.h>
#include <platform.h>
#ifndef MAX_FDS
/** Maximum number of open files/directories */
# define MAX_FDS 16
#endif
//#if WITH_HFS
extern const struct fs_ops hfs_fs_ops;
//#endif
static const struct fs_struct fs_list[] = {
//#if WITH_HFS
{ "hfs", &hfs_fs_ops },
//#endif
{ NULL, NULL }
};
struct fscookie {
const struct fs_struct *fs;
void *ctxt;
};
#define FILEMAGIC 'film'
#define DIRMAGIC 'dirm'
struct filecookie {
struct list_node node;
struct fscookie *fscookie;
int fd;
void *ctxt;
bool is_dir;
};
struct fsmount {
struct list_node node;
struct fscookie *cookie;
size_t mountpoint_len;
char mountpoint[256];
};
/** list of active mountpoints */
static struct list_node mounts = LIST_INITIAL_VALUE(mounts);
/** list of open files */
static struct list_node files = LIST_INITIAL_VALUE(files);
/**
* Given a file descriptor, obtain the corresponding filecookie.
*
* \param[in] handle The file descriptor to look up.
*
* \return Pointer to the corresponding filecookie structure.
* \retval NULL The file descriptor is not currently allocated.
*/
static struct filecookie *fhtocp(int handle)
{
struct filecookie *file;
list_for_every_entry(&files, file, struct filecookie, node) {
if (file->fd == handle)
return file;
}
return NULL;
}
/**
* Find a valid, unused file descriptor.
*
* \return A file descriptor not currently in use.
* \retval -1 No file descriptors are available.
*/
static int getfd(void)
{
int fd;
for (fd = 0; fd < MAX_FDS; fd++) {
if (NULL == fhtocp(fd))
return fd;
}
return -1;
}
struct fsmount *path_to_mount(const char *path, const char **outpath)
{
struct fsmount *mount;
/* take a path and see if it lies on a mountpoint */
list_for_every_entry(&mounts, mount, struct fsmount, node) {
if (!strncmp(path, mount->mountpoint, mount->mountpoint_len)) {
if (path[mount->mountpoint_len] == '/') {
/* the leading chars matched the mountpoint exactly and the next char was a / */
*outpath = &path[mount->mountpoint_len];
// printf("path_to_mount: path '%s, found mount %p, path '%s', outpath '%s'\n",
// path, mount, mount->mountpoint, *outpath);
return mount;
}
if (path[mount->mountpoint_len] == 0) {
/* the leading chars matched the mountpoint and there was no more string,
* return a / for the file system to chew on */
*outpath = "/";
return mount;
}
}
}
return NULL;
}
int fs_mount(const char *dev, const char *fs_name, const char *mountpoint)
{
struct fscookie *cookie;
struct fsmount *mount;
const struct fs_struct *fs;
void *ctxt;
int err;
ASSERT(dev);
ASSERT(fs_name);
for (fs = fs_list; fs->name != NULL; fs++) {
if (!strcmp(fs_name, fs->name))
break;
}
if (fs->name == NULL) {
dprintf(DEBUG_CRITICAL, "fs_mount: fs_name '%s' not in registered fs list\n", fs_name);
return -1;
}
list_for_every_entry(&mounts, mount, struct fsmount, node) {
if (!strcmp(mountpoint, mount->mountpoint)) {
dprintf(DEBUG_CRITICAL, "fs_mount: mountpoint '%s' already occupied\n", mountpoint);
return -1;
}
}
err = fs->ops->mount(&ctxt, dev);
if (err < 0)
return err;
cookie = malloc(sizeof(struct fscookie));
cookie->fs = fs;
cookie->ctxt = ctxt;
/* create a mount structure */
mount = malloc(sizeof(struct fsmount));
mount->cookie = cookie;
strlcpy(mount->mountpoint, mountpoint, sizeof(mount->mountpoint));
mount->mountpoint_len = strlen(mount->mountpoint); // used in path comparisons
list_add_tail(&mounts, &mount->node);
return 0;
}
void fs_unmount(const char *dev)
{
struct fsmount *mount;
const char *shortpath;
ASSERT(dev);
mount = path_to_mount(dev, &shortpath);
if (!mount)
return;
ASSERT(mount->cookie != NULL);
mount->cookie->fs->ops->unmount(mount->cookie->ctxt);
/* remove the mount structure */
list_delete(&mount->node);
free(mount->cookie);
free(mount);
}
int fs_fsstat(void *fscookie, struct statfs *stat)
{
struct fscookie *cookie = (struct fscookie *)fscookie;
ASSERT(cookie);
ASSERT(stat);
return cookie->fs->ops->fsstat(cookie->ctxt, stat);
}
int fs_open(const char *path, uint8_t flags)
{
struct fsmount *mount;
void *ctxt;
struct filecookie *fcookie;
const char *shortpath;
int fd;
ASSERT(path);
mount = path_to_mount(path, &shortpath);
if (!mount)
return -1;
if (-1 == (fd = getfd()))
return -1;
ctxt = mount->cookie->fs->ops->open(mount->cookie->ctxt, shortpath, flags);
if (!ctxt)
return -1;
fcookie = malloc(sizeof(struct filecookie));
fcookie->fscookie = mount->cookie;
fcookie->ctxt = ctxt;
fcookie->is_dir = false;
fcookie->fd = fd;
list_add_head(&files, &fcookie->node);
return fcookie->fd;
}
int fs_read(int handle, void *buf, size_t len)
{
struct filecookie *fcookie = fhtocp(handle);
ASSERT(fcookie);
ASSERT(!fcookie->is_dir);
return fcookie->fscookie->fs->ops->read(fcookie->ctxt, buf, len);
}
int fs_stat(int handle, struct stat *st)
{
struct filecookie *fcookie = fhtocp(handle);
ASSERT(fcookie);
ASSERT(!fcookie->is_dir);
return fcookie->fscookie->fs->ops->fstat(fcookie->ctxt, st);
}
off_t fs_seek(int handle, off_t offset, int whence)
{
struct filecookie *fcookie = fhtocp(handle);
ASSERT(fcookie);
ASSERT(!fcookie->is_dir);
return fcookie->fscookie->fs->ops->seek(fcookie->ctxt, offset, whence);
}
int fs_close(int handle)
{
struct filecookie *fcookie = fhtocp(handle);
ASSERT(fcookie);
ASSERT(!fcookie->is_dir);
ASSERT(fcookie->fscookie != NULL);
ASSERT(fcookie->fscookie->fs != NULL);
ASSERT(fcookie->fscookie->fs->ops != NULL);
ASSERT(fcookie->fscookie->fs->ops->close != NULL);
fcookie->fscookie->fs->ops->close(fcookie->ctxt);
list_delete(&fcookie->node);
free(fcookie);
return 0;
}
int fs_opendir(const char *path)
{
struct fsmount *mount;
void *ctxt;
struct filecookie *fcookie;
const char *shortpath;
int fd;
mount = path_to_mount(path, &shortpath);
if (!mount)
return -1;
if (-1 == (fd = getfd()))
return -1;
ctxt = mount->cookie->fs->ops->opendir(mount->cookie->ctxt, shortpath);
if (!ctxt)
return -1;
fcookie = malloc(sizeof(struct filecookie));
fcookie->fscookie = mount->cookie;
fcookie->ctxt = ctxt;
fcookie->is_dir = true;
fcookie->fd = fd;
list_add_head(&files, &fcookie->node);
return fcookie->fd;
}
int fs_readdir(int handle, struct dirent *ent)
{
struct filecookie *fcookie = fhtocp(handle);
RELEASE_ASSERT(fcookie);
RELEASE_ASSERT(fcookie->is_dir);
return fcookie->fscookie->fs->ops->readdir(fcookie->ctxt, ent);
}
int fs_rewinddir(int handle)
{
struct filecookie *fcookie = fhtocp(handle);
RELEASE_ASSERT(fcookie);
RELEASE_ASSERT(fcookie->is_dir);
fcookie->fscookie->fs->ops->rewinddir(fcookie->ctxt);
return 0;
}
int fs_closedir(int handle)
{
struct filecookie *fcookie = fhtocp(handle);
RELEASE_ASSERT(fcookie);
RELEASE_ASSERT(fcookie->is_dir);
fcookie->fscookie->fs->ops->closedir(fcookie->ctxt);
list_delete(&fcookie->node);
free(fcookie);
return 0;
}
int fs_load_file(const char *path, addr_t addr, size_t *maxsize)
{
int filefd;
int result;
struct stat stat;
RELEASE_ASSERT(maxsize != NULL);
/* open the file */
filefd = fs_open(path, 0);
if (filefd < 0) {
platform_record_breadcrumb("fs_load_file", "fs_open_failed");
return -1;
}
/* stat it */
fs_stat(filefd, &stat);
/* make sure it is not too large */
if (*maxsize < stat.st_size) {
platform_record_breadcrumb("fs_load_file", "maxsize_exceeded");
return -1;
}
if (!security_allow_memory((void *)addr, stat.st_size)) {
platform_record_breadcrumb("fs_load_file", "permission_denied");
printf("Permission Denied\n");
return -1;
}
/* read the entire thing in */
result = fs_read(filefd, (void *)addr, stat.st_size);
fs_close(filefd);
if (result < 0 || (unsigned)result != stat.st_size) {
platform_record_breadcrumb("fs_load_file", "read_failed");
// Zero the buffer in case of a partial read
memset((void *)addr, *maxsize, 0);
return -1;
}
*maxsize = stat.st_size;
return 0;
}
void fs_dump_mounts(void)
{
struct fsmount *mount;
printf("active fs mounts:\n");
list_for_every_entry(&mounts, mount, struct fsmount, node) {
printf("\t'%s'\n", mount->mountpoint);
}
}
const char *fs_get_fsname(const int index)
{
int i;
for (i = 0; i < index; i++)
if (fs_list[i].name == NULL)
return(NULL);
return(fs_list[i].name);
}