#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/disklabel.h>
#include <sys/time.h>
#include <ufs/ufs/dinode.h>
#include <ufs/ufs/dir.h>
#include <ufs/ffs/fs.h>
#include "stand.h"
#include "string.h"
static int ufs_open(const char *, struct open_file *);
static int ufs_write(struct open_file *, const void *, size_t, size_t *);
static int ufs_close(struct open_file *);
static int ufs_read(struct open_file *, void *, size_t, size_t *);
static off_t ufs_seek(struct open_file *, off_t, int);
static int ufs_stat(struct open_file *, struct stat *);
static int ufs_readdir(struct open_file *, struct dirent *);
struct fs_ops ufs_fsops = {
"ufs",
ufs_open,
ufs_close,
ufs_read,
ufs_write,
ufs_seek,
ufs_stat,
ufs_readdir
};
struct file {
off_t f_seekp;
struct fs *f_fs;
union dinode {
struct ufs1_dinode di1;
struct ufs2_dinode di2;
} f_di;
int f_nindir[NIADDR];
char *f_blk[NIADDR];
size_t f_blksize[NIADDR];
ufs2_daddr_t f_blkno[NIADDR];
ufs2_daddr_t f_buf_blkno;
char *f_buf;
size_t f_buf_size;
};
#define DIP(fp, field) \
((fp)->f_fs->fs_magic == FS_UFS1_MAGIC ? \
(fp)->f_di.di1.field : (fp)->f_di.di2.field)
static int read_inode(ino_t, struct open_file *);
static int block_map(struct open_file *, ufs2_daddr_t, ufs2_daddr_t *);
static int buf_read_file(struct open_file *, char **, size_t *);
static int buf_write_file(struct open_file *, const char *, size_t *);
static int search_directory(char *, struct open_file *, ino_t *);
static int
read_inode(ino_t inumber, struct open_file *f)
{
struct file *fp = (struct file *)f->f_fsdata;
struct fs *fs = fp->f_fs;
char *buf;
size_t rsize;
int rc;
if (fs == NULL)
panic("fs == NULL");
buf = malloc(fs->fs_bsize);
twiddle(1);
rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
fsbtodb(fs, ino_to_fsba(fs, inumber)), fs->fs_bsize,
buf, &rsize);
if (rc)
goto out;
if (rsize != fs->fs_bsize) {
rc = EIO;
goto out;
}
if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
fp->f_di.di1 = ((struct ufs1_dinode *)buf)
[ino_to_fsbo(fs, inumber)];
else
fp->f_di.di2 = ((struct ufs2_dinode *)buf)
[ino_to_fsbo(fs, inumber)];
{
int level;
for (level = 0; level < NIADDR; level++)
fp->f_blkno[level] = -1;
fp->f_buf_blkno = -1;
}
fp->f_seekp = 0;
out:
free(buf);
return (rc);
}
static int
block_map(struct open_file *f, ufs2_daddr_t file_block,
ufs2_daddr_t *disk_block_p)
{
struct file *fp = (struct file *)f->f_fsdata;
struct fs *fs = fp->f_fs;
int level;
int idx;
ufs2_daddr_t ind_block_num;
int rc;
if (file_block < NDADDR) {
*disk_block_p = DIP(fp, di_db[file_block]);
return (0);
}
file_block -= NDADDR;
for (level = 0; level < NIADDR; level++) {
if (file_block < fp->f_nindir[level])
break;
file_block -= fp->f_nindir[level];
}
if (level == NIADDR) {
return (EFBIG);
}
ind_block_num = DIP(fp, di_ib[level]);
for (; level >= 0; level--) {
if (ind_block_num == 0) {
*disk_block_p = 0;
return (0);
}
if (fp->f_blkno[level] != ind_block_num) {
if (fp->f_blk[level] == NULL)
fp->f_blk[level] =
malloc(fs->fs_bsize);
twiddle(1);
rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
fsbtodb(fp->f_fs, ind_block_num),
fs->fs_bsize,
fp->f_blk[level],
&fp->f_blksize[level]);
if (rc)
return (rc);
if (fp->f_blksize[level] != fs->fs_bsize)
return (EIO);
fp->f_blkno[level] = ind_block_num;
}
if (level > 0) {
idx = file_block / fp->f_nindir[level - 1];
file_block %= fp->f_nindir[level - 1];
} else
idx = file_block;
if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
ind_block_num = ((ufs1_daddr_t *)fp->f_blk[level])[idx];
else
ind_block_num = ((ufs2_daddr_t *)fp->f_blk[level])[idx];
}
*disk_block_p = ind_block_num;
return (0);
}
static int
buf_write_file(struct open_file *f, const char *buf_p, size_t *size_p)
{
struct file *fp = (struct file *)f->f_fsdata;
struct fs *fs = fp->f_fs;
long off;
ufs_lbn_t file_block;
ufs2_daddr_t disk_block;
size_t block_size;
int rc;
off = blkoff(fs, fp->f_seekp);
file_block = lblkno(fs, fp->f_seekp);
block_size = sblksize(fs, DIP(fp, di_size), file_block);
rc = block_map(f, file_block, &disk_block);
if (rc)
return (rc);
if (disk_block == 0)
return (EFBIG);
if (*size_p > DIP(fp, di_size) - fp->f_seekp)
*size_p = DIP(fp, di_size) - fp->f_seekp;
if (*size_p > block_size - off)
*size_p = block_size - off;
if (((off > 0) || (*size_p + off < block_size)) &&
(file_block != fp->f_buf_blkno)) {
if (fp->f_buf == (char *)0)
fp->f_buf = malloc(fs->fs_bsize);
twiddle(8);
rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
fsbtodb(fs, disk_block),
block_size, fp->f_buf, &fp->f_buf_size);
if (rc)
return (rc);
fp->f_buf_blkno = file_block;
}
bcopy(buf_p, fp->f_buf + off, *size_p);
twiddle(4);
rc = (f->f_dev->dv_strategy)(f->f_devdata, F_WRITE,
fsbtodb(fs, disk_block),
block_size, fp->f_buf, &fp->f_buf_size);
return (rc);
}
static int
buf_read_file(struct open_file *f, char **buf_p, size_t *size_p)
{
struct file *fp = (struct file *)f->f_fsdata;
struct fs *fs = fp->f_fs;
long off;
ufs_lbn_t file_block;
ufs2_daddr_t disk_block;
size_t block_size;
int rc;
off = blkoff(fs, fp->f_seekp);
file_block = lblkno(fs, fp->f_seekp);
block_size = sblksize(fs, DIP(fp, di_size), file_block);
if (file_block != fp->f_buf_blkno) {
if (fp->f_buf == (char *)0)
fp->f_buf = malloc(fs->fs_bsize);
rc = block_map(f, file_block, &disk_block);
if (rc)
return (rc);
if (disk_block == 0) {
bzero(fp->f_buf, block_size);
fp->f_buf_size = block_size;
} else {
twiddle(4);
rc = (f->f_dev->dv_strategy)(f->f_devdata,
F_READ, fsbtodb(fs, disk_block),
block_size, fp->f_buf, &fp->f_buf_size);
if (rc)
return (rc);
}
fp->f_buf_blkno = file_block;
}
*buf_p = fp->f_buf + off;
*size_p = block_size - off;
if (*size_p > DIP(fp, di_size) - fp->f_seekp)
*size_p = DIP(fp, di_size) - fp->f_seekp;
return (0);
}
static int
search_directory(char *name, struct open_file *f, ino_t *inumber_p)
{
struct file *fp = (struct file *)f->f_fsdata;
struct direct *dp;
struct direct *edp;
char *buf;
size_t buf_size;
int namlen, length;
int rc;
length = strlen(name);
fp->f_seekp = 0;
while (fp->f_seekp < DIP(fp, di_size)) {
rc = buf_read_file(f, &buf, &buf_size);
if (rc)
return (rc);
dp = (struct direct *)buf;
edp = (struct direct *)(buf + buf_size);
while (dp < edp) {
if (dp->d_ino == (ino_t)0)
goto next;
namlen = dp->d_namlen;
if (namlen == length &&
strcmp(name, dp->d_name) == 0) {
*inumber_p = dp->d_ino;
return (0);
}
next:
dp = (struct direct *)((char *)dp + dp->d_reclen);
}
fp->f_seekp += buf_size;
}
return (ENOENT);
}
static int sblock_try[] = SBLOCKSEARCH;
static int
ufs_open(const char *upath, struct open_file *f)
{
char *cp, *ncp;
int c;
ino_t inumber, parent_inumber;
struct file *fp;
struct fs *fs;
int i, rc;
size_t buf_size;
int nlinks = 0;
char namebuf[MAXPATHLEN+1];
char *buf = NULL;
char *path = NULL;
fp = malloc(sizeof (struct file));
bzero(fp, sizeof (struct file));
f->f_fsdata = (void *)fp;
fs = malloc(SBLOCKSIZE);
fp->f_fs = fs;
twiddle(1);
for (i = 0; sblock_try[i] != -1; i++) {
rc = (f->f_dev->dv_strategy)(f->f_devdata, F_READ,
sblock_try[i] / DEV_BSIZE, SBLOCKSIZE,
(char *)fs, &buf_size);
if (rc)
goto out;
if ((fs->fs_magic == FS_UFS1_MAGIC ||
(fs->fs_magic == FS_UFS2_MAGIC &&
fs->fs_sblockloc == sblock_try[i])) &&
buf_size == SBLOCKSIZE &&
fs->fs_bsize <= MAXBSIZE &&
fs->fs_bsize >= sizeof (struct fs))
break;
}
if (sblock_try[i] == -1) {
rc = EINVAL;
goto out;
}
{
ufs2_daddr_t mult;
int level;
mult = 1;
for (level = 0; level < NIADDR; level++) {
mult *= NINDIR(fs);
fp->f_nindir[level] = mult;
}
}
inumber = ROOTINO;
if ((rc = read_inode(inumber, f)) != 0)
goto out;
cp = path = strdup(upath);
if (path == NULL) {
rc = ENOMEM;
goto out;
}
while (*cp) {
while (*cp == '/')
cp++;
if (*cp == '\0')
break;
if ((DIP(fp, di_mode) & IFMT) != IFDIR) {
rc = ENOTDIR;
goto out;
}
{
int len = 0;
ncp = cp;
while ((c = *cp) != '\0' && c != '/') {
if (++len > UFS_MAXNAMLEN) {
rc = ENOENT;
goto out;
}
cp++;
}
*cp = '\0';
}
parent_inumber = inumber;
rc = search_directory(ncp, f, &inumber);
*cp = c;
if (rc)
goto out;
if ((rc = read_inode(inumber, f)) != 0)
goto out;
if ((DIP(fp, di_mode) & IFMT) == IFLNK) {
int link_len = DIP(fp, di_size);
int len;
len = strlen(cp);
if (link_len + len > MAXPATHLEN ||
++nlinks > MAXSYMLINKS) {
rc = ENOENT;
goto out;
}
bcopy(cp, &namebuf[link_len], len + 1);
if (link_len < fs->fs_maxsymlinklen) {
if (fp->f_fs->fs_magic == FS_UFS1_MAGIC)
cp = (caddr_t)(fp->f_di.di1.di_db);
else
cp = (caddr_t)(fp->f_di.di2.di_db);
bcopy(cp, namebuf, (unsigned)link_len);
} else {
size_t buf_size;
ufs2_daddr_t disk_block;
struct fs *fs = fp->f_fs;
if (!buf)
buf = malloc(fs->fs_bsize);
rc = block_map(f, (ufs2_daddr_t)0, &disk_block);
if (rc)
goto out;
twiddle(1);
rc = (f->f_dev->dv_strategy)(f->f_devdata,
F_READ, fsbtodb(fs, disk_block),
fs->fs_bsize, buf, &buf_size);
if (rc)
goto out;
bcopy((char *)buf, namebuf, (unsigned)link_len);
}
cp = namebuf;
if (*cp != '/')
inumber = parent_inumber;
else
inumber = (ino_t)ROOTINO;
if ((rc = read_inode(inumber, f)) != 0)
goto out;
}
}
rc = 0;
fp->f_seekp = 0;
out:
if (buf)
free(buf);
if (path)
free(path);
if (rc) {
if (fp->f_buf)
free(fp->f_buf);
free(fp->f_fs);
free(fp);
}
return (rc);
}
static int
ufs_close(struct open_file *f)
{
struct file *fp = (struct file *)f->f_fsdata;
int level;
f->f_fsdata = (void *)0;
if (fp == (struct file *)0)
return (0);
for (level = 0; level < NIADDR; level++) {
if (fp->f_blk[level])
free(fp->f_blk[level]);
}
if (fp->f_buf)
free(fp->f_buf);
free(fp->f_fs);
free(fp);
return (0);
}
static int
ufs_read(struct open_file *f, void *start, size_t size, size_t *resid)
{
struct file *fp = (struct file *)f->f_fsdata;
size_t csize;
char *buf;
size_t buf_size;
int rc = 0;
char *addr = start;
while (size != 0) {
if (fp->f_seekp >= DIP(fp, di_size))
break;
rc = buf_read_file(f, &buf, &buf_size);
if (rc)
break;
csize = size;
if (csize > buf_size)
csize = buf_size;
bcopy(buf, addr, csize);
fp->f_seekp += csize;
addr += csize;
size -= csize;
}
if (resid)
*resid = size;
return (rc);
}
static int
ufs_write(struct open_file *f, const void *start, size_t size, size_t *resid)
{
struct file *fp = (struct file *)f->f_fsdata;
size_t csize;
int rc = 0;
const char *addr = start;
csize = size;
while ((size != 0) && (csize != 0)) {
if (fp->f_seekp >= DIP(fp, di_size))
break;
if (csize >= 512) csize = 512;
rc = buf_write_file(f, addr, &csize);
if (rc)
break;
fp->f_seekp += csize;
addr += csize;
size -= csize;
}
if (resid)
*resid = size;
return (rc);
}
static off_t
ufs_seek(struct open_file *f, off_t offset, int where)
{
struct file *fp = (struct file *)f->f_fsdata;
switch (where) {
case SEEK_SET:
fp->f_seekp = offset;
break;
case SEEK_CUR:
fp->f_seekp += offset;
break;
case SEEK_END:
fp->f_seekp = DIP(fp, di_size) - offset;
break;
default:
errno = EINVAL;
return (-1);
}
return (fp->f_seekp);
}
static int
ufs_stat(struct open_file *f, struct stat *sb)
{
struct file *fp = (struct file *)f->f_fsdata;
sb->st_mode = DIP(fp, di_mode);
sb->st_uid = DIP(fp, di_uid);
sb->st_gid = DIP(fp, di_gid);
sb->st_size = DIP(fp, di_size);
return (0);
}
static int
ufs_readdir(struct open_file *f, struct dirent *d)
{
struct file *fp = (struct file *)f->f_fsdata;
struct direct *dp;
char *buf;
size_t buf_size;
int error;
again:
if (fp->f_seekp >= DIP(fp, di_size))
return (ENOENT);
error = buf_read_file(f, &buf, &buf_size);
if (error)
return (error);
dp = (struct direct *)buf;
fp->f_seekp += dp->d_reclen;
if (dp->d_ino == (ino_t)0)
goto again;
d->d_type = 0;
strcpy(d->d_name, dp->d_name);
return (0);
}