mdb_fdio.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* File Descriptor I/O Backend
*
* Simple backend to pass though io_ops to the corresponding system calls on
* an underlying fd. We provide functions to create fdio objects using file
* descriptors, explicit file names, and path lookups. We save the complete
* filename so that mdb_iob_name can be used to report the complete filename
* of an open macro file in syntax error messages.
*/
#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]; /* Save filename for error messages */
int fd_fd; /* File descriptor */
} 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;
if (path != NULL && strchr(fname, '/') == NULL) {
char buf[MAXPATHLEN];
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 = {
fdio_read,
fdio_write,
fdio_seek,
fdio_ctl,
fdio_close,
fdio_name,
no_io_link,
no_io_unlink,
no_io_setattr,
no_io_suspend,
no_io_resume
};
/*
* In order to read from a block-oriented device, we pick up the seek pointer,
* read each containing block, and then copy the desired range of bytes back
* into the caller's buffer. Unfortunately Solaris hardcodes the notion of
* DEV_BSIZE as the transfer unit for such devices; no ioctl() to obtain the
* transfer unit dynamically is currently available. At the end of the
* transfer we reset the seek pointer to where the caller thinks it should be.
*/
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;
uchar_t blk[DEV_BSIZE];
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); /* errno is set for us */
while (resid != 0) {
off64_t devoff = off & ~(DEV_BSIZE - 1);
size_t blkoff = off & (DEV_BSIZE - 1);
size_t len = MIN(resid, DEV_BSIZE - blkoff);
if (pread64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
break; /* errno is set for us, unless EOF */
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);
}
/*
* To perform a write to a block-oriented device, we use the same basic
* algorithm as fdio_bdev_read(), above. In the inner loop, we read an
* entire block, modify it using the data from the caller's buffer, and
* then write the entire block back to the device.
*/
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;
uchar_t blk[DEV_BSIZE];
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); /* errno is set for us */
while (resid != 0) {
off64_t devoff = off & ~(DEV_BSIZE - 1);
size_t blkoff = off & (DEV_BSIZE - 1);
size_t len = MIN(resid, DEV_BSIZE - blkoff);
if (pread64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
break; /* errno is set for us, unless EOF */
bcopy(buf, &blk[blkoff], len);
if (pwrite64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
break; /* errno is set for us, unless EOF */
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 = {
fdio_bdev_read,
fdio_bdev_write,
fdio_seek,
fdio_ctl,
fdio_close,
fdio_name,
no_io_link,
no_io_unlink,
no_io_setattr,
no_io_suspend,
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;
/*
* We determine if something is a raw block-oriented disk device by
* testing to see if it is a character device that supports DKIOCINFO.
* If we are operating on a disk in raw mode, we must do our own
* block-oriented i/o; otherwise we can just use read() and write().
*/
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);
}