| /* |
| * CDDL HEADER START |
| * |
| * The contents of this file are subject to the terms of the |
| * Common Development and Distribution License (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 2008 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; |
| 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 = { |
| 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); |
| } |
| |
| int |
| mdb_fdio_fileno(mdb_io_t *io) |
| { |
| fd_data_t *fdp = io->io_data; |
| return (fdp->fd_fd); |
| } |