#include <sys/types.h>
#include "stand.h"
#include "host_syscall.h"
#include "kboot.h"
#define HOST_PATH_MAX 1025
extern struct devsw host_dev;
const char *hostfs_root = "/";
enum FTYPE {
regular,
dir,
};
typedef struct _hostfs_file {
enum FTYPE hf_type;
int hf_fd;
char hf_dents[2048];
char *hf_curdent;
int hf_dentlen;
} hostfs_file;
static hostfs_file *
hostfs_alloc(void)
{
hostfs_file *hf;
hf = malloc(sizeof(*hf));
if (hf != NULL)
memset(hf, 0, sizeof(*hf));
return (hf);
}
static void
hostfs_free(hostfs_file *hf)
{
free(hf);
}
static int
hostfs_open(const char *fn, struct open_file *f)
{
hostfs_file *hf;
struct host_kstat ksb;
char path[HOST_PATH_MAX];
if (f->f_dev != &host_dev) {
return (EINVAL);
}
if (strncmp("/sys/", fn, 5) == 0 || strncmp("/proc/", fn, 6) == 0)
strlcpy(path, fn, sizeof(path));
else if (fn[0] == '/' && fn[1] == '/' && fn[2] != '/')
strlcpy(path, fn + 1, sizeof(path));
else
snprintf(path, sizeof(path), "%s/%s", hostfs_root, fn);
hf = hostfs_alloc();
hf->hf_fd = host_open(path, HOST_O_RDONLY, 0);
if (hf->hf_fd < 0) {
hostfs_free(hf);
return (EINVAL);
}
if (host_fstat(hf->hf_fd, &ksb) < 0) {
hostfs_free(hf);
return (EINVAL);
}
if (S_ISDIR(hf->hf_fd)) {
hf->hf_type = dir;
} else {
hf->hf_type = regular;
}
f->f_fsdata = hf;
return (0);
}
static int
hostfs_close(struct open_file *f)
{
hostfs_file *hf = f->f_fsdata;
host_close(hf->hf_fd);
hostfs_free(hf);
f->f_fsdata = NULL;
return (0);
}
static int
hostfs_read(struct open_file *f, void *start, size_t size, size_t *resid)
{
hostfs_file *hf = f->f_fsdata;
ssize_t sz;
sz = host_read(hf->hf_fd, start, size);
if (sz < 0)
return (host_to_stand_errno(sz));
*resid = size - sz;
return (0);
}
static off_t
hostfs_seek(struct open_file *f, off_t offset, int whence)
{
hostfs_file *hf = f->f_fsdata;
uint32_t offl, offh;
long err;
uint64_t res;
res = (uint64_t)offset;
offl = res & 0xfffffffful;
offh = (res >> 32) & 0xfffffffful;
err = host_llseek(hf->hf_fd, offh, offl, &res, whence);
if (is_linux_error(err)) {
errno = host_to_stand_errno(err);
return (-1);
}
return (res);
}
static int
hostfs_stat(struct open_file *f, struct stat *sb)
{
struct host_kstat ksb;
hostfs_file *hf = f->f_fsdata;
if (host_fstat(hf->hf_fd, &ksb) < 0)
return (EINVAL);
memset(sb, 0, sizeof(*sb));
sb->st_dev = ksb.st_dev;
sb->st_ino = ksb.st_ino;
sb->st_nlink = ksb.st_nlink;
sb->st_mode = ksb.st_mode;
sb->st_uid = ksb.st_uid;
sb->st_gid = ksb.st_gid;
sb->st_rdev = ksb.st_rdev;
sb->st_atim.tv_sec = ksb.st_atime_sec;
sb->st_atim.tv_nsec = ksb.st_atime_nsec;
sb->st_mtim.tv_sec = ksb.st_mtime_sec;
sb->st_mtim.tv_nsec = ksb.st_mtime_nsec;
sb->st_ctim.tv_sec = ksb.st_ctime_sec;
sb->st_ctim.tv_nsec = ksb.st_ctime_nsec;
sb->st_size = ksb.st_size;
sb->st_blocks = ksb.st_blocks;
sb->st_blksize = ksb.st_blksize;
return (0);
}
static int
hostfs_readdir(struct open_file *f, struct dirent *d)
{
hostfs_file *hf = f->f_fsdata;
int dentlen;
struct host_dirent64 *dent;
if (hf->hf_curdent == NULL) {
dentlen = host_getdents64(hf->hf_fd, hf->hf_dents, sizeof(hf->hf_dents));
if (dentlen <= 0)
return (EINVAL);
hf->hf_dentlen = dentlen;
hf->hf_curdent = hf->hf_dents;
}
dent = (struct host_dirent64 *)hf->hf_curdent;
d->d_fileno = dent->d_ino;
d->d_type = dent->d_type;
strlcpy(d->d_name, dent->d_name, sizeof(d->d_name));
d->d_namlen = strlen(d->d_name);
hf->hf_curdent += dent->d_reclen;
if (hf->hf_curdent >= hf->hf_dents + hf->hf_dentlen) {
hf->hf_curdent = NULL;
hf->hf_dentlen = 0;
}
return (0);
}
struct fs_ops hostfs_fsops = {
.fs_name = "host",
.fo_open = hostfs_open,
.fo_close = hostfs_close,
.fo_read = hostfs_read,
.fo_write = null_write,
.fo_seek = hostfs_seek,
.fo_stat = hostfs_stat,
.fo_readdir = hostfs_readdir
};
static int
host_dev_init(void)
{
return (0);
}
static int
host_dev_print(int verbose)
{
char line[80];
printf("%s devices:", host_dev.dv_name);
if (pager_output("\n") != 0)
return (1);
snprintf(line, sizeof(line), " host%d: Host filesystem\n", 0);
return (pager_output(line));
}
static int
host_dev_open(struct open_file *f, ...)
{
return (0);
}
static int
host_dev_close(struct open_file *f)
{
return (0);
}
static int
host_dev_strategy(void *devdata, int rw, daddr_t dblk, size_t size,
char *buf, size_t *rsize)
{
return (ENOSYS);
}
struct devsw host_dev = {
.dv_name = "host",
.dv_type = DEVT_NET,
.dv_init = host_dev_init,
.dv_strategy = host_dev_strategy,
.dv_open = host_dev_open,
.dv_close = host_dev_close,
.dv_ioctl = noioctl,
.dv_print = host_dev_print,
.dv_cleanup = NULL
};