#include <sys/types.h>
#include <sys/param.h>
#include <sys/vnode.h>
#include <sys/fs/ufs_fsdir.h>
#include <sys/fs/ufs_fs.h>
#include <sys/fs/ufs_inode.h>
#include <sys/sysmacros.h>
#include <sys/bootvfs.h>
#include <sys/filep.h>
#include <sys/kmem.h>
#include <sys/kobj.h>
#include <sys/sunddi.h>
extern void *bkmem_alloc(size_t);
extern void bkmem_free(void *, size_t);
extern int cf_check_compressed(fileid_t *);
extern void cf_close(fileid_t *);
extern void cf_seek(fileid_t *, off_t, int);
extern int cf_read(fileid_t *, caddr_t, size_t);
extern int bootrd_debug;
static fileid_t *head;
devid_t *ufs_devp;
struct dirinfo {
int loc;
fileid_t *fi;
};
static int bufs_close(int);
static void bufs_closeall(int);
static ino_t find(fileid_t *filep, char *path);
static ino_t dlook(fileid_t *filep, char *path);
static daddr32_t sbmap(fileid_t *filep, daddr32_t bn);
static struct direct *readdir(struct dirinfo *dstuff);
static void set_cache(int, void *, uint_t);
static void *get_cache(int);
static void free_cache();
static int
openi(fileid_t *filep, ino_t inode)
{
struct dinode *dp;
devid_t *devp = filep->fi_devp;
filep->fi_inode = get_cache((int)inode);
if (filep->fi_inode != 0)
return (0);
filep->fi_offset = 0;
filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs,
itod(&devp->un_fs.di_fs, inode));
filep->fi_count = devp->un_fs.di_fs.fs_bsize;
filep->fi_memp = 0;
if (diskread(filep) != 0) {
return (0);
}
dp = (struct dinode *)filep->fi_memp;
filep->fi_inode = (struct inode *)
bkmem_alloc(sizeof (struct inode));
bzero((char *)filep->fi_inode, sizeof (struct inode));
filep->fi_inode->i_ic =
dp[itoo(&devp->un_fs.di_fs, inode)].di_un.di_icom;
filep->fi_inode->i_number = inode;
set_cache((int)inode, (void *)filep->fi_inode, sizeof (struct inode));
return (0);
}
static fileid_t *
find_fp(int fd)
{
fileid_t *filep = head;
if (fd >= 0) {
while ((filep = filep->fi_forw) != head)
if (fd == filep->fi_filedes)
return (filep->fi_taken ? filep : 0);
}
return (0);
}
static ino_t
find(fileid_t *filep, char *path)
{
char *q;
char c;
ino_t inode;
char lpath[MAXPATHLEN];
char *lpathp = lpath;
int len, r;
devid_t *devp;
inode = 0;
if (path == NULL || *path == '\0') {
kobj_printf("null path\n");
return (inode);
}
if (bootrd_debug)
kobj_printf("openi: %s\n", path);
bzero(lpath, sizeof (lpath));
bcopy(path, lpath, strlen(path));
devp = filep->fi_devp;
while (*lpathp) {
r = (lpathp == lpath);
if (r && openi(filep, (ino_t)UFSROOTINO))
return ((ino_t)0);
while (*lpathp == '/')
lpathp++;
q = lpathp;
while (*q != '/' && *q != '\0')
q++;
c = *q;
*q = '\0';
if (r && (*lpathp == '\0'))
return ((ino_t)UFSROOTINO);
if ((inode = dlook(filep, lpathp)) != 0) {
if (openi(filep, inode))
return ((ino_t)0);
if ((filep->fi_inode->i_smode & IFMT) == IFLNK) {
filep->fi_blocknum =
fsbtodb(&devp->un_fs.di_fs,
filep->fi_inode->i_db[0]);
filep->fi_count = DEV_BSIZE;
filep->fi_memp = 0;
if (diskread(filep) != 0)
return ((ino_t)0);
len = strlen(filep->fi_memp);
if (filep->fi_memp[0] == '/')
lpathp = lpath;
bcopy(q, lpathp + len, strlen(q + 1) + 2);
*(lpathp + len) = c;
bcopy(filep->fi_memp, lpathp, len);
lpathp = lpath;
continue;
} else
*q = c;
if (c == '\0')
break;
lpathp = q;
continue;
} else {
return ((ino_t)0);
}
}
return (inode);
}
static daddr32_t
sbmap(fileid_t *filep, daddr32_t bn)
{
struct inode *inodep;
int i, j, sh;
daddr32_t nb, *bap;
daddr32_t *db;
devid_t *devp;
devp = filep->fi_devp;
inodep = filep->fi_inode;
db = inodep->i_db;
if (bn < NDADDR) {
nb = db[bn];
return (nb);
}
sh = 1;
bn -= NDADDR;
for (j = NIADDR; j > 0; j--) {
sh *= NINDIR(&devp->un_fs.di_fs);
if (bn < sh)
break;
bn -= sh;
}
if (j == 0) {
return ((daddr32_t)0);
}
nb = inodep->i_ib[NIADDR - j];
if (nb == 0) {
return ((daddr32_t)0);
}
for (; j <= NIADDR; j++) {
filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, nb);
filep->fi_count = devp->un_fs.di_fs.fs_bsize;
filep->fi_memp = 0;
if (diskread(filep) != 0)
return (0);
bap = (daddr32_t *)filep->fi_memp;
sh /= NINDIR(&devp->un_fs.di_fs);
i = (bn / sh) % NINDIR(&devp->un_fs.di_fs);
nb = bap[i];
if (nb == 0) {
return ((daddr32_t)0);
}
}
return (nb);
}
static ino_t
dlook(fileid_t *filep, char *path)
{
struct direct *dp;
struct inode *ip;
struct dirinfo dirp;
int len;
ip = filep->fi_inode;
if (path == NULL || *path == '\0')
return (0);
if (bootrd_debug)
kobj_printf("dlook: %s\n", path);
if ((ip->i_smode & IFMT) != IFDIR) {
return (0);
}
if (ip->i_size == 0) {
return (0);
}
len = strlen(path);
dirp.loc = 0;
dirp.fi = filep;
for (dp = readdir(&dirp); dp != NULL; dp = readdir(&dirp)) {
if (dp->d_ino == 0)
continue;
if (dp->d_namlen == len && strcmp(path, dp->d_name) == 0) {
return (dp->d_ino);
}
if (strcmp(path, "*") == 0 && bootrd_debug)
kobj_printf("%s\n", dp->d_name);
}
return (0);
}
struct direct *
readdir(struct dirinfo *dstuff)
{
struct direct *dp;
fileid_t *filep;
daddr32_t lbn, d;
int off;
devid_t *devp;
filep = dstuff->fi;
devp = filep->fi_devp;
for (;;) {
if (dstuff->loc >= filep->fi_inode->i_size) {
return (NULL);
}
off = blkoff(&devp->un_fs.di_fs, dstuff->loc);
if (bootrd_debug)
kobj_printf("readdir: off = 0x%x\n", off);
if (off == 0) {
lbn = lblkno(&devp->un_fs.di_fs, dstuff->loc);
d = sbmap(filep, lbn);
if (d == 0)
return (NULL);
filep->fi_blocknum = fsbtodb(&devp->un_fs.di_fs, d);
filep->fi_count =
blksize(&devp->un_fs.di_fs, filep->fi_inode, lbn);
filep->fi_memp = 0;
if (diskread(filep) != 0) {
return (NULL);
}
}
dp = (struct direct *)(filep->fi_memp + off);
dstuff->loc += dp->d_reclen;
if (dp->d_ino == 0)
continue;
if (bootrd_debug)
kobj_printf("readdir: name = %s\n", dp->d_name);
return (dp);
}
}
static int
getblock(fileid_t *filep, caddr_t buf, int count, int *rcount)
{
struct fs *fs;
caddr_t p;
int off, size, diff;
daddr32_t lbn;
devid_t *devp;
if (bootrd_debug)
kobj_printf("getblock: buf 0x%p, count 0x%x\n",
(void *)buf, count);
devp = filep->fi_devp;
p = filep->fi_memp;
if ((signed)filep->fi_count <= 0) {
diff = filep->fi_inode->i_size - filep->fi_offset;
if (diff <= 0) {
kobj_printf("Short read\n");
return (-1);
}
fs = &devp->un_fs.di_fs;
lbn = lblkno(fs, filep->fi_offset);
filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn));
off = blkoff(fs, filep->fi_offset);
size = blksize(fs, filep->fi_inode, lbn);
filep->fi_count = size;
filep->fi_memp = filep->fi_buf;
*rcount = 0;
if (off == 0 && count >= size) {
filep->fi_memp = buf;
if (diskread(filep)) {
return (-1);
}
*rcount = size;
filep->fi_count = 0;
return (0);
} else if (diskread(filep))
return (-1);
if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
filep->fi_count = diff + off;
filep->fi_count -= off;
p = &filep->fi_memp[off];
}
filep->fi_memp = p;
return (0);
}
static int
getblock_noopt(fileid_t *filep)
{
struct fs *fs;
caddr_t p;
int off, size, diff;
daddr32_t lbn;
devid_t *devp;
if (bootrd_debug)
kobj_printf("getblock_noopt: start\n");
devp = filep->fi_devp;
p = filep->fi_memp;
if ((signed)filep->fi_count <= 0) {
diff = filep->fi_inode->i_size - filep->fi_offset;
if (diff <= 0) {
kobj_printf("Short read\n");
return (-1);
}
fs = &devp->un_fs.di_fs;
lbn = lblkno(fs, filep->fi_offset);
filep->fi_blocknum = fsbtodb(fs, sbmap(filep, lbn));
off = blkoff(fs, filep->fi_offset);
size = blksize(fs, filep->fi_inode, lbn);
filep->fi_count = size;
filep->fi_memp = NULL;
if (diskread(filep))
return (-1);
if (filep->fi_offset - off + size >= filep->fi_inode->i_size)
filep->fi_count = diff + off;
filep->fi_count -= off;
p = &filep->fi_memp[off];
}
filep->fi_memp = p;
return (0);
}
static ssize_t
bufs_read(int fd, caddr_t buf, size_t count)
{
size_t i, j;
caddr_t n;
int rcount;
fileid_t *filep;
if (!(filep = find_fp(fd))) {
return (-1);
}
if ((filep->fi_flags & FI_COMPRESSED) == 0 &&
filep->fi_offset + count > filep->fi_inode->i_size)
count = filep->fi_inode->i_size - filep->fi_offset;
if ((i = count) == 0)
return (0);
n = buf;
while (i > 0) {
if (filep->fi_flags & FI_COMPRESSED) {
int rval;
if ((rval = cf_read(filep, buf, count)) < 0)
return (0);
j = (size_t)rval;
if (j < i)
i = j;
} else {
if ((j = filep->fi_count) == 0) {
(void) getblock(filep, buf, i, &rcount);
i -= rcount;
buf += rcount;
filep->fi_offset += rcount;
continue;
} else {
j = MIN(i, j);
bcopy(filep->fi_memp, buf, (unsigned)j);
}
}
buf += j;
filep->fi_memp += j;
filep->fi_offset += j;
filep->fi_count -= j;
i -= j;
}
return (buf - n);
}
static int
bufs_mountroot(char *str)
{
if (ufs_devp)
return (0);
ufs_devp = (devid_t *)bkmem_alloc(sizeof (devid_t));
ufs_devp->di_taken = 1;
ufs_devp->di_dcookie = 0;
ufs_devp->di_desc = (char *)bkmem_alloc(strlen(str) + 1);
(void) strcpy(ufs_devp->di_desc, str);
bzero(ufs_devp->un_fs.dummy, SBSIZE);
head = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
head->fi_back = head->fi_forw = head;
head->fi_filedes = 0;
head->fi_taken = 0;
head->fi_devp = ufs_devp;
head->fi_blocknum = SBLOCK;
head->fi_count = (uint_t)SBSIZE;
head->fi_memp = (caddr_t)&(ufs_devp->un_fs.di_fs);
head->fi_offset = 0;
if (diskread(head)) {
kobj_printf("failed to read superblock\n");
(void) bufs_closeall(1);
return (-1);
}
if (ufs_devp->un_fs.di_fs.fs_magic != FS_MAGIC) {
if (bootrd_debug)
kobj_printf("fs magic = 0x%x\n",
ufs_devp->un_fs.di_fs.fs_magic);
(void) bufs_closeall(1);
return (-1);
}
if (bootrd_debug)
kobj_printf("mountroot succeeded\n");
return (0);
}
static int
bufs_unmountroot(void)
{
if (ufs_devp == NULL)
return (-1);
(void) bufs_closeall(1);
return (0);
}
static int
bufs_open(char *filename, int flags __unused)
{
fileid_t *filep;
ino_t inode;
static int filedes = 1;
if (bootrd_debug)
kobj_printf("open: %s\n", filename);
filep = (fileid_t *)bkmem_alloc(sizeof (fileid_t));
filep->fi_back = head->fi_back;
filep->fi_forw = head;
head->fi_back->fi_forw = filep;
head->fi_back = filep;
filep->fi_filedes = filedes++;
filep->fi_taken = 1;
filep->fi_path = (char *)bkmem_alloc(strlen(filename) + 1);
(void) strcpy(filep->fi_path, filename);
filep->fi_devp = ufs_devp;
filep->fi_inode = NULL;
bzero(filep->fi_buf, MAXBSIZE);
filep->fi_getblock = getblock_noopt;
filep->fi_flags = 0;
inode = find(filep, (char *)filename);
if (inode == 0) {
if (bootrd_debug)
kobj_printf("open: cannot find %s\n", filename);
(void) bufs_close(filep->fi_filedes);
return (-1);
}
if (openi(filep, inode)) {
kobj_printf("open: cannot open %s\n", filename);
(void) bufs_close(filep->fi_filedes);
return (-1);
}
filep->fi_offset = filep->fi_count = 0;
if (cf_check_compressed(filep) != 0)
return (-1);
return (filep->fi_filedes);
}
static off_t
bufs_lseek(int fd, off_t addr, int whence)
{
fileid_t *filep;
if (!(filep = find_fp(fd)))
return (-1);
if (filep->fi_flags & FI_COMPRESSED) {
cf_seek(filep, addr, whence);
} else {
switch (whence) {
case SEEK_CUR:
filep->fi_offset += addr;
break;
case SEEK_SET:
filep->fi_offset = addr;
break;
default:
case SEEK_END:
kobj_printf("lseek(): invalid whence value %d\n",
whence);
break;
}
filep->fi_blocknum = addr / DEV_BSIZE;
}
filep->fi_count = 0;
return (0);
}
int
bufs_fstat(int fd, struct bootstat *stp)
{
fileid_t *filep;
struct inode *ip;
if (!(filep = find_fp(fd)))
return (-1);
ip = filep->fi_inode;
stp->st_mode = 0;
stp->st_size = 0;
if (ip == NULL)
return (0);
switch (ip->i_smode & IFMT) {
case IFLNK:
stp->st_mode = S_IFLNK;
break;
case IFREG:
stp->st_mode = S_IFREG;
break;
default:
break;
}
stp->st_size = ip->i_size;
stp->st_atim.tv_sec = ip->i_atime.tv_sec;
stp->st_atim.tv_nsec = ip->i_atime.tv_usec * 1000;
stp->st_mtim.tv_sec = ip->i_mtime.tv_sec;
stp->st_mtim.tv_nsec = ip->i_mtime.tv_usec * 1000;
stp->st_ctim.tv_sec = ip->i_ctime.tv_sec;
stp->st_ctim.tv_nsec = ip->i_ctime.tv_usec * 1000;
return (0);
}
static int
bufs_close(int fd)
{
fileid_t *filep;
if (!(filep = find_fp(fd)))
return (-1);
if (filep->fi_taken && (filep != head)) {
bkmem_free(filep->fi_path, strlen(filep->fi_path)+1);
filep->fi_blocknum = filep->fi_count = filep->fi_offset = 0;
filep->fi_memp = (caddr_t)0;
filep->fi_devp = 0;
filep->fi_taken = 0;
filep->fi_forw->fi_back = filep->fi_back;
filep->fi_back->fi_forw = filep->fi_forw;
cf_close(filep);
bkmem_free((char *)filep, sizeof (fileid_t));
if (kmem_ready == 0)
free_cache();
return (0);
} else {
kobj_printf("\nFile descrip %d not allocated!", fd);
return (-1);
}
}
static void
bufs_closeall(int flag __unused)
{
fileid_t *filep = head;
while ((filep = filep->fi_forw) != head)
if (filep->fi_taken)
if (bufs_close(filep->fi_filedes))
kobj_printf(
"Filesystem may be inconsistent.\n");
ufs_devp->di_taken = 0;
bkmem_free((char *)ufs_devp, sizeof (devid_t));
bkmem_free((char *)head, sizeof (fileid_t));
ufs_devp = NULL;
head = NULL;
free_cache();
}
static struct cache {
struct cache *next;
void *data;
int key;
uint_t size;
} *icache;
void
set_cache(int key, void *data, uint_t size)
{
struct cache *entry = bkmem_alloc(sizeof (*entry));
entry->key = key;
entry->data = data;
entry->size = size;
if (icache) {
entry->next = icache;
icache = entry;
} else {
icache = entry;
entry->next = 0;
}
}
void *
get_cache(int key)
{
struct cache *entry = icache;
while (entry) {
if (entry->key == key)
return (entry->data);
entry = entry->next;
}
return (NULL);
}
void
free_cache()
{
struct cache *next, *entry = icache;
while (entry) {
next = entry->next;
bkmem_free(entry->data, entry->size);
bkmem_free(entry, sizeof (*entry));
entry = next;
}
icache = 0;
}
struct boot_fs_ops bufs_ops = {
"boot_ufs",
bufs_mountroot,
bufs_unmountroot,
bufs_open,
bufs_close,
bufs_read,
bufs_lseek,
bufs_fstat,
NULL
};