#include <sys/param.h>
#include <sys/stat.h>
#include <sys/dkio.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <mdb/mdb_modapi.h>
#include <mdb/mdb_err.h>
#include <mdb/mdb_io_impl.h>
#include <mdb/mdb.h>
typedef struct fd_data {
char fd_name[MAXPATHLEN];
int fd_fd;
} fd_data_t;
static ssize_t
fdio_read(mdb_io_t *io, void *buf, size_t nbytes)
{
fd_data_t *fdp = io->io_data;
if (io->io_next == NULL)
return (read(fdp->fd_fd, buf, nbytes));
return (IOP_READ(io->io_next, buf, nbytes));
}
static ssize_t
fdio_write(mdb_io_t *io, const void *buf, size_t nbytes)
{
fd_data_t *fdp = io->io_data;
if (io->io_next == NULL)
return (write(fdp->fd_fd, buf, nbytes));
return (IOP_WRITE(io->io_next, buf, nbytes));
}
static off64_t
fdio_seek(mdb_io_t *io, off64_t offset, int whence)
{
fd_data_t *fdp = io->io_data;
if (io->io_next == NULL)
return (lseek64(fdp->fd_fd, offset, whence));
return (IOP_SEEK(io->io_next, offset, whence));
}
static int
fdio_ctl(mdb_io_t *io, int req, void *arg)
{
fd_data_t *fdp = io->io_data;
if (io->io_next != NULL)
return (IOP_CTL(io->io_next, req, arg));
if (req == MDB_IOC_GETFD)
return (fdp->fd_fd);
else
return (ioctl(fdp->fd_fd, req, arg));
}
static void
fdio_close(mdb_io_t *io)
{
fd_data_t *fdp = io->io_data;
(void) close(fdp->fd_fd);
mdb_free(fdp, sizeof (fd_data_t));
}
static const char *
fdio_name(mdb_io_t *io)
{
fd_data_t *fdp = io->io_data;
if (io->io_next == NULL)
return (fdp->fd_name);
return (IOP_NAME(io->io_next));
}
mdb_io_t *
mdb_fdio_create_path(const char *path[], const char *fname,
int flags, mode_t mode)
{
int fd;
char buf[MAXPATHLEN];
if (path != NULL && strchr(fname, '/') == NULL) {
int i;
for (fd = -1, i = 0; path[i] != NULL; i++) {
(void) mdb_iob_snprintf(buf, MAXPATHLEN, "%s/%s",
path[i], fname);
if (access(buf, F_OK) == 0) {
fd = open64(buf, flags, mode);
fname = buf;
break;
}
}
if (fd == -1)
(void) set_errno(ENOENT);
} else
fd = open64(fname, flags, mode);
if (fd >= 0)
return (mdb_fdio_create_named(fd, fname));
return (NULL);
}
static const mdb_io_ops_t fdio_file_ops = {
.io_read = fdio_read,
.io_write = fdio_write,
.io_seek = fdio_seek,
.io_ctl = fdio_ctl,
.io_close = fdio_close,
.io_name = fdio_name,
.io_link = no_io_link,
.io_unlink = no_io_unlink,
.io_setattr = no_io_setattr,
.io_suspend = no_io_suspend,
.io_resume = no_io_resume
};
static uint_t
fdio_bdev_info(int fd)
{
struct dk_minfo disk_info;
if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
return (DEV_BSIZE);
return (disk_info.dki_lbsize);
}
static ssize_t
fdio_bdev_read(mdb_io_t *io, void *buf, size_t nbytes)
{
fd_data_t *fdp = io->io_data;
ssize_t resid = nbytes;
size_t blksize;
uchar_t *blk;
off64_t off;
if (io->io_next != NULL)
return (IOP_READ(io->io_next, buf, nbytes));
if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
return (-1);
blksize = fdio_bdev_info(fdp->fd_fd);
blk = mdb_zalloc(blksize, UM_SLEEP | UM_GC);
while (resid != 0) {
off64_t devoff = off & ~(blksize - 1);
size_t blkoff = off & (blksize - 1);
size_t len = MIN(resid, blksize - blkoff);
if (pread64(fdp->fd_fd, blk, blksize, devoff) != blksize)
break;
bcopy(&blk[blkoff], buf, len);
resid -= len;
off += len;
buf = (char *)buf + len;
}
if (resid == nbytes && nbytes != 0)
return (set_errno(EMDB_EOF));
(void) lseek64(fdp->fd_fd, off, SEEK_SET);
return (nbytes - resid);
}
static ssize_t
fdio_bdev_write(mdb_io_t *io, const void *buf, size_t nbytes)
{
fd_data_t *fdp = io->io_data;
ssize_t resid = nbytes;
size_t blksize;
uchar_t *blk;
off64_t off;
if (io->io_next != NULL)
return (IOP_WRITE(io->io_next, buf, nbytes));
if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
return (-1);
blksize = fdio_bdev_info(fdp->fd_fd);
blk = mdb_zalloc(blksize, UM_SLEEP | UM_GC);
while (resid != 0) {
off64_t devoff = off & ~(blksize - 1);
size_t blkoff = off & (blksize - 1);
size_t len = MIN(resid, blksize - blkoff);
if (pread64(fdp->fd_fd, blk, blksize, devoff) != blksize)
break;
bcopy(buf, &blk[blkoff], len);
if (pwrite64(fdp->fd_fd, blk, blksize, devoff) != blksize)
break;
resid -= len;
off += len;
buf = (char *)buf + len;
}
if (resid == nbytes && nbytes != 0)
return (set_errno(EMDB_EOF));
(void) lseek64(fdp->fd_fd, off, SEEK_SET);
return (nbytes - resid);
}
static const mdb_io_ops_t fdio_bdev_ops = {
.io_read = fdio_bdev_read,
.io_write = fdio_bdev_write,
.io_seek = fdio_seek,
.io_ctl = fdio_ctl,
.io_close = fdio_close,
.io_name = fdio_name,
.io_link = no_io_link,
.io_unlink = no_io_unlink,
.io_setattr = no_io_setattr,
.io_suspend = no_io_suspend,
.io_resume = no_io_resume,
};
mdb_io_t *
mdb_fdio_create(int fd)
{
mdb_io_t *io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
fd_data_t *fdp = mdb_alloc(sizeof (fd_data_t), UM_SLEEP);
struct dk_cinfo info;
struct stat64 st;
switch (fd) {
case STDIN_FILENO:
(void) strcpy(fdp->fd_name, "(stdin)");
break;
case STDOUT_FILENO:
(void) strcpy(fdp->fd_name, "(stdout)");
break;
case STDERR_FILENO:
(void) strcpy(fdp->fd_name, "(stderr)");
break;
default:
(void) mdb_iob_snprintf(fdp->fd_name, MAXPATHLEN, "fd %d", fd);
}
fdp->fd_fd = fd;
if (fstat64(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
ioctl(fd, DKIOCINFO, &info) == 0)
io->io_ops = &fdio_bdev_ops;
else
io->io_ops = &fdio_file_ops;
io->io_data = fdp;
io->io_next = NULL;
io->io_refcnt = 0;
return (io);
}
mdb_io_t *
mdb_fdio_create_named(int fd, const char *name)
{
mdb_io_t *io = mdb_fdio_create(fd);
fd_data_t *fdp = io->io_data;
(void) strncpy(fdp->fd_name, name, MAXPATHLEN);
fdp->fd_name[MAXPATHLEN - 1] = '\0';
return (io);
}
int
mdb_fdio_fileno(mdb_io_t *io)
{
fd_data_t *fdp = io->io_data;
return (fdp->fd_fd);
}