17c478bdstevel@tonic-gate/*
27c478bdstevel@tonic-gate * CDDL HEADER START
37c478bdstevel@tonic-gate *
47c478bdstevel@tonic-gate * The contents of this file are subject to the terms of the
57f4f06dpetede * Common Development and Distribution License (the "License").
67f4f06dpetede * You may not use this file except in compliance with the License.
77c478bdstevel@tonic-gate *
87c478bdstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bdstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bdstevel@tonic-gate * See the License for the specific language governing permissions
117c478bdstevel@tonic-gate * and limitations under the License.
127c478bdstevel@tonic-gate *
137c478bdstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bdstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bdstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bdstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bdstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bdstevel@tonic-gate *
197c478bdstevel@tonic-gate * CDDL HEADER END
207c478bdstevel@tonic-gate */
217c478bdstevel@tonic-gate/*
22a576ab5rab * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
237c478bdstevel@tonic-gate * Use is subject to license terms.
247c478bdstevel@tonic-gate */
257c478bdstevel@tonic-gate
267c478bdstevel@tonic-gate/*
277c478bdstevel@tonic-gate * File Descriptor I/O Backend
287c478bdstevel@tonic-gate *
297c478bdstevel@tonic-gate * Simple backend to pass though io_ops to the corresponding system calls on
307c478bdstevel@tonic-gate * an underlying fd.  We provide functions to create fdio objects using file
317c478bdstevel@tonic-gate * descriptors, explicit file names, and path lookups.  We save the complete
327c478bdstevel@tonic-gate * filename so that mdb_iob_name can be used to report the complete filename
337c478bdstevel@tonic-gate * of an open macro file in syntax error messages.
347c478bdstevel@tonic-gate */
357c478bdstevel@tonic-gate
367c478bdstevel@tonic-gate#include <sys/param.h>
377c478bdstevel@tonic-gate#include <sys/stat.h>
387c478bdstevel@tonic-gate#include <sys/dkio.h>
397c478bdstevel@tonic-gate#include <unistd.h>
407c478bdstevel@tonic-gate#include <string.h>
417c478bdstevel@tonic-gate#include <errno.h>
427c478bdstevel@tonic-gate#include <fcntl.h>
437c478bdstevel@tonic-gate
447c478bdstevel@tonic-gate#include <mdb/mdb_modapi.h>
457c478bdstevel@tonic-gate#include <mdb/mdb_err.h>
467c478bdstevel@tonic-gate#include <mdb/mdb_io_impl.h>
477c478bdstevel@tonic-gate#include <mdb/mdb.h>
487c478bdstevel@tonic-gate
497c478bdstevel@tonic-gatetypedef struct fd_data {
507c478bdstevel@tonic-gate	char fd_name[MAXPATHLEN];	/* Save filename for error messages */
517c478bdstevel@tonic-gate	int fd_fd;			/* File descriptor */
527c478bdstevel@tonic-gate} fd_data_t;
537c478bdstevel@tonic-gate
547c478bdstevel@tonic-gatestatic ssize_t
557c478bdstevel@tonic-gatefdio_read(mdb_io_t *io, void *buf, size_t nbytes)
567c478bdstevel@tonic-gate{
577c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
587c478bdstevel@tonic-gate
597c478bdstevel@tonic-gate	if (io->io_next == NULL)
607c478bdstevel@tonic-gate		return (read(fdp->fd_fd, buf, nbytes));
617c478bdstevel@tonic-gate
627c478bdstevel@tonic-gate	return (IOP_READ(io->io_next, buf, nbytes));
637c478bdstevel@tonic-gate}
647c478bdstevel@tonic-gate
657c478bdstevel@tonic-gatestatic ssize_t
667c478bdstevel@tonic-gatefdio_write(mdb_io_t *io, const void *buf, size_t nbytes)
677c478bdstevel@tonic-gate{
687c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
697c478bdstevel@tonic-gate
707c478bdstevel@tonic-gate	if (io->io_next == NULL)
717c478bdstevel@tonic-gate		return (write(fdp->fd_fd, buf, nbytes));
727c478bdstevel@tonic-gate
737c478bdstevel@tonic-gate	return (IOP_WRITE(io->io_next, buf, nbytes));
747c478bdstevel@tonic-gate}
757c478bdstevel@tonic-gate
767c478bdstevel@tonic-gatestatic off64_t
777c478bdstevel@tonic-gatefdio_seek(mdb_io_t *io, off64_t offset, int whence)
787c478bdstevel@tonic-gate{
797c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
807c478bdstevel@tonic-gate
817c478bdstevel@tonic-gate	if (io->io_next == NULL)
827c478bdstevel@tonic-gate		return (lseek64(fdp->fd_fd, offset, whence));
837c478bdstevel@tonic-gate
847c478bdstevel@tonic-gate	return (IOP_SEEK(io->io_next, offset, whence));
857c478bdstevel@tonic-gate}
867c478bdstevel@tonic-gate
877c478bdstevel@tonic-gatestatic int
887c478bdstevel@tonic-gatefdio_ctl(mdb_io_t *io, int req, void *arg)
897c478bdstevel@tonic-gate{
907c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
917c478bdstevel@tonic-gate
927c478bdstevel@tonic-gate	if (io->io_next != NULL)
937c478bdstevel@tonic-gate		return (IOP_CTL(io->io_next, req, arg));
947c478bdstevel@tonic-gate
957c478bdstevel@tonic-gate	if (req == MDB_IOC_GETFD)
967c478bdstevel@tonic-gate		return (fdp->fd_fd);
977c478bdstevel@tonic-gate	else
987c478bdstevel@tonic-gate		return (ioctl(fdp->fd_fd, req, arg));
997c478bdstevel@tonic-gate}
1007c478bdstevel@tonic-gate
1017c478bdstevel@tonic-gatestatic void
1027c478bdstevel@tonic-gatefdio_close(mdb_io_t *io)
1037c478bdstevel@tonic-gate{
1047c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
1057c478bdstevel@tonic-gate
1067c478bdstevel@tonic-gate	(void) close(fdp->fd_fd);
1077c478bdstevel@tonic-gate	mdb_free(fdp, sizeof (fd_data_t));
1087c478bdstevel@tonic-gate}
1097c478bdstevel@tonic-gate
1107c478bdstevel@tonic-gatestatic const char *
1117c478bdstevel@tonic-gatefdio_name(mdb_io_t *io)
1127c478bdstevel@tonic-gate{
1137c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
1147c478bdstevel@tonic-gate
1157c478bdstevel@tonic-gate	if (io->io_next == NULL)
1167c478bdstevel@tonic-gate		return (fdp->fd_name);
1177c478bdstevel@tonic-gate
1187c478bdstevel@tonic-gate	return (IOP_NAME(io->io_next));
1197c478bdstevel@tonic-gate}
1207c478bdstevel@tonic-gate
1217c478bdstevel@tonic-gatemdb_io_t *
1227c478bdstevel@tonic-gatemdb_fdio_create_path(const char *path[], const char *fname,
1237c478bdstevel@tonic-gate    int flags, mode_t mode)
1247c478bdstevel@tonic-gate{
1257c478bdstevel@tonic-gate	int fd;
1267f4f06dpetede	char buf[MAXPATHLEN];
1277c478bdstevel@tonic-gate
1287c478bdstevel@tonic-gate	if (path != NULL && strchr(fname, '/') == NULL) {
1297c478bdstevel@tonic-gate		int i;
1307c478bdstevel@tonic-gate
1317c478bdstevel@tonic-gate		for (fd = -1, i = 0; path[i] != NULL; i++) {
1327c478bdstevel@tonic-gate			(void) mdb_iob_snprintf(buf, MAXPATHLEN, "%s/%s",
1337c478bdstevel@tonic-gate			    path[i], fname);
1347c478bdstevel@tonic-gate
1357c478bdstevel@tonic-gate			if (access(buf, F_OK) == 0) {
1367c478bdstevel@tonic-gate				fd = open64(buf, flags, mode);
1377c478bdstevel@tonic-gate				fname = buf;
1387c478bdstevel@tonic-gate				break;
1397c478bdstevel@tonic-gate			}
1407c478bdstevel@tonic-gate		}
1417c478bdstevel@tonic-gate
1427c478bdstevel@tonic-gate		if (fd == -1)
1437c478bdstevel@tonic-gate			(void) set_errno(ENOENT);
1447c478bdstevel@tonic-gate	} else
1457c478bdstevel@tonic-gate		fd = open64(fname, flags, mode);
1467c478bdstevel@tonic-gate
1477c478bdstevel@tonic-gate	if (fd >= 0)
1487c478bdstevel@tonic-gate		return (mdb_fdio_create_named(fd, fname));
1497c478bdstevel@tonic-gate
1507c478bdstevel@tonic-gate	return (NULL);
1517c478bdstevel@tonic-gate}
1527c478bdstevel@tonic-gate
1537c478bdstevel@tonic-gatestatic const mdb_io_ops_t fdio_file_ops = {
1547c478bdstevel@tonic-gate	fdio_read,
1557c478bdstevel@tonic-gate	fdio_write,
1567c478bdstevel@tonic-gate	fdio_seek,
1577c478bdstevel@tonic-gate	fdio_ctl,
1587c478bdstevel@tonic-gate	fdio_close,
1597c478bdstevel@tonic-gate	fdio_name,
1607c478bdstevel@tonic-gate	no_io_link,
1617c478bdstevel@tonic-gate	no_io_unlink,
1627c478bdstevel@tonic-gate	no_io_setattr,
1637c478bdstevel@tonic-gate	no_io_suspend,
1647c478bdstevel@tonic-gate	no_io_resume
1657c478bdstevel@tonic-gate};
1667c478bdstevel@tonic-gate
1677c478bdstevel@tonic-gate/*
168b55a30dToomas Soome * Read media logical block size. On error, return DEV_BSIZE.
169b55a30dToomas Soome */
170b55a30dToomas Soomestatic uint_t
171b55a30dToomas Soomefdio_bdev_info(int fd)
172b55a30dToomas Soome{
173b55a30dToomas Soome	struct dk_minfo disk_info;
174b55a30dToomas Soome
175b55a30dToomas Soome	if ((ioctl(fd, DKIOCGMEDIAINFO, (caddr_t)&disk_info)) == -1)
176b55a30dToomas Soome		return (DEV_BSIZE);
177b55a30dToomas Soome
178b55a30dToomas Soome	return (disk_info.dki_lbsize);
179b55a30dToomas Soome}
180b55a30dToomas Soome
181b55a30dToomas Soome/*
1827c478bdstevel@tonic-gate * In order to read from a block-oriented device, we pick up the seek pointer,
1837c478bdstevel@tonic-gate * read each containing block, and then copy the desired range of bytes back
184b55a30dToomas Soome * into the caller's buffer. At the end of the transfer we reset the seek
185b55a30dToomas Soome * pointer to where the caller thinks it should be.
1867c478bdstevel@tonic-gate */
1877c478bdstevel@tonic-gatestatic ssize_t
1887c478bdstevel@tonic-gatefdio_bdev_read(mdb_io_t *io, void *buf, size_t nbytes)
1897c478bdstevel@tonic-gate{
1907c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
1917c478bdstevel@tonic-gate	ssize_t resid = nbytes;
192b55a30dToomas Soome	size_t blksize;
193b55a30dToomas Soome	uchar_t *blk;
1947c478bdstevel@tonic-gate	off64_t off;
1957c478bdstevel@tonic-gate
1967c478bdstevel@tonic-gate	if (io->io_next != NULL)
1977c478bdstevel@tonic-gate		return (IOP_READ(io->io_next, buf, nbytes));
1987c478bdstevel@tonic-gate
1997c478bdstevel@tonic-gate	if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
2007c478bdstevel@tonic-gate		return (-1); /* errno is set for us */
2017c478bdstevel@tonic-gate
202b55a30dToomas Soome	blksize = fdio_bdev_info(fdp->fd_fd);
203b55a30dToomas Soome	blk = mdb_zalloc(blksize, UM_SLEEP | UM_GC);
2047c478bdstevel@tonic-gate	while (resid != 0) {
205b55a30dToomas Soome		off64_t devoff = off & ~(blksize - 1);
206b55a30dToomas Soome		size_t blkoff = off & (blksize - 1);
207b55a30dToomas Soome		size_t len = MIN(resid, blksize - blkoff);
2087c478bdstevel@tonic-gate
209b55a30dToomas Soome		if (pread64(fdp->fd_fd, blk, blksize, devoff) != blksize)
2107c478bdstevel@tonic-gate			break; /* errno is set for us, unless EOF */
2117c478bdstevel@tonic-gate
2127c478bdstevel@tonic-gate		bcopy(&blk[blkoff], buf, len);
2137c478bdstevel@tonic-gate		resid -= len;
2147c478bdstevel@tonic-gate		off += len;
2157c478bdstevel@tonic-gate		buf = (char *)buf + len;
2167c478bdstevel@tonic-gate	}
2177c478bdstevel@tonic-gate
2187c478bdstevel@tonic-gate	if (resid == nbytes && nbytes != 0)
2197c478bdstevel@tonic-gate		return (set_errno(EMDB_EOF));
2207c478bdstevel@tonic-gate
2217c478bdstevel@tonic-gate	(void) lseek64(fdp->fd_fd, off, SEEK_SET);
2227c478bdstevel@tonic-gate	return (nbytes - resid);
2237c478bdstevel@tonic-gate}
2247c478bdstevel@tonic-gate
2257c478bdstevel@tonic-gate/*
2267c478bdstevel@tonic-gate * To perform a write to a block-oriented device, we use the same basic
2277c478bdstevel@tonic-gate * algorithm as fdio_bdev_read(), above.  In the inner loop, we read an
2287c478bdstevel@tonic-gate * entire block, modify it using the data from the caller's buffer, and
2297c478bdstevel@tonic-gate * then write the entire block back to the device.
2307c478bdstevel@tonic-gate */
2317c478bdstevel@tonic-gatestatic ssize_t
2327c478bdstevel@tonic-gatefdio_bdev_write(mdb_io_t *io, const void *buf, size_t nbytes)
2337c478bdstevel@tonic-gate{
2347c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
2357c478bdstevel@tonic-gate	ssize_t resid = nbytes;
236b55a30dToomas Soome	size_t blksize;
237b55a30dToomas Soome	uchar_t *blk;
2387c478bdstevel@tonic-gate	off64_t off;
2397c478bdstevel@tonic-gate
2407c478bdstevel@tonic-gate	if (io->io_next != NULL)
2417c478bdstevel@tonic-gate		return (IOP_WRITE(io->io_next, buf, nbytes));
2427c478bdstevel@tonic-gate
2437c478bdstevel@tonic-gate	if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
2447c478bdstevel@tonic-gate		return (-1); /* errno is set for us */
2457c478bdstevel@tonic-gate
246b55a30dToomas Soome	blksize = fdio_bdev_info(fdp->fd_fd);
247b55a30dToomas Soome	blk = mdb_zalloc(blksize, UM_SLEEP | UM_GC);
2487c478bdstevel@tonic-gate	while (resid != 0) {
249b55a30dToomas Soome		off64_t devoff = off & ~(blksize - 1);
250b55a30dToomas Soome		size_t blkoff = off & (blksize - 1);
251b55a30dToomas Soome		size_t len = MIN(resid, blksize - blkoff);
2527c478bdstevel@tonic-gate
253b55a30dToomas Soome		if (pread64(fdp->fd_fd, blk, blksize, devoff) != blksize)
2547c478bdstevel@tonic-gate			break; /* errno is set for us, unless EOF */
2557c478bdstevel@tonic-gate
2567c478bdstevel@tonic-gate		bcopy(buf, &blk[blkoff], len);
2577c478bdstevel@tonic-gate
258b55a30dToomas Soome		if (pwrite64(fdp->fd_fd, blk, blksize, devoff) != blksize)
2597c478bdstevel@tonic-gate			break; /* errno is set for us, unless EOF */
2607c478bdstevel@tonic-gate
2617c478bdstevel@tonic-gate		resid -= len;
2627c478bdstevel@tonic-gate		off += len;
2637c478bdstevel@tonic-gate		buf = (char *)buf + len;
2647c478bdstevel@tonic-gate	}
2657c478bdstevel@tonic-gate
2667c478bdstevel@tonic-gate	if (resid == nbytes && nbytes != 0)
2677c478bdstevel@tonic-gate		return (set_errno(EMDB_EOF));
2687c478bdstevel@tonic-gate
2697c478bdstevel@tonic-gate	(void) lseek64(fdp->fd_fd, off, SEEK_SET);
2707c478bdstevel@tonic-gate	return (nbytes - resid);
2717c478bdstevel@tonic-gate}
2727c478bdstevel@tonic-gate
2737c478bdstevel@tonic-gatestatic const mdb_io_ops_t fdio_bdev_ops = {
2747c478bdstevel@tonic-gate	fdio_bdev_read,
2757c478bdstevel@tonic-gate	fdio_bdev_write,
2767c478bdstevel@tonic-gate	fdio_seek,
2777c478bdstevel@tonic-gate	fdio_ctl,
2787c478bdstevel@tonic-gate	fdio_close,
2797c478bdstevel@tonic-gate	fdio_name,
2807c478bdstevel@tonic-gate	no_io_link,
2817c478bdstevel@tonic-gate	no_io_unlink,
2827c478bdstevel@tonic-gate	no_io_setattr,
2837c478bdstevel@tonic-gate	no_io_suspend,
2847c478bdstevel@tonic-gate	no_io_resume
2857c478bdstevel@tonic-gate};
2867c478bdstevel@tonic-gate
2877c478bdstevel@tonic-gatemdb_io_t *
2887c478bdstevel@tonic-gatemdb_fdio_create(int fd)
2897c478bdstevel@tonic-gate{
2907c478bdstevel@tonic-gate	mdb_io_t *io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
2917c478bdstevel@tonic-gate	fd_data_t *fdp = mdb_alloc(sizeof (fd_data_t), UM_SLEEP);
2927c478bdstevel@tonic-gate
2937c478bdstevel@tonic-gate	struct dk_cinfo info;
2947c478bdstevel@tonic-gate	struct stat64 st;
2957c478bdstevel@tonic-gate
2967c478bdstevel@tonic-gate	switch (fd) {
2977c478bdstevel@tonic-gate	case STDIN_FILENO:
2987c478bdstevel@tonic-gate		(void) strcpy(fdp->fd_name, "(stdin)");
2997c478bdstevel@tonic-gate		break;
3007c478bdstevel@tonic-gate	case STDOUT_FILENO:
3017c478bdstevel@tonic-gate		(void) strcpy(fdp->fd_name, "(stdout)");
3027c478bdstevel@tonic-gate		break;
3037c478bdstevel@tonic-gate	case STDERR_FILENO:
3047c478bdstevel@tonic-gate		(void) strcpy(fdp->fd_name, "(stderr)");
3057c478bdstevel@tonic-gate		break;
3067c478bdstevel@tonic-gate	default:
3077c478bdstevel@tonic-gate		(void) mdb_iob_snprintf(fdp->fd_name, MAXPATHLEN, "fd %d", fd);
3087c478bdstevel@tonic-gate	}
3097c478bdstevel@tonic-gate
3107c478bdstevel@tonic-gate	fdp->fd_fd = fd;
3117c478bdstevel@tonic-gate
3127c478bdstevel@tonic-gate	/*
3137c478bdstevel@tonic-gate	 * We determine if something is a raw block-oriented disk device by
3147c478bdstevel@tonic-gate	 * testing to see if it is a character device that supports DKIOCINFO.
3157c478bdstevel@tonic-gate	 * If we are operating on a disk in raw mode, we must do our own
3167c478bdstevel@tonic-gate	 * block-oriented i/o; otherwise we can just use read() and write().
3177c478bdstevel@tonic-gate	 */
3187c478bdstevel@tonic-gate	if (fstat64(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
3197c478bdstevel@tonic-gate	    ioctl(fd, DKIOCINFO, &info) == 0)
3207c478bdstevel@tonic-gate		io->io_ops = &fdio_bdev_ops;
3217c478bdstevel@tonic-gate	else
3227c478bdstevel@tonic-gate		io->io_ops = &fdio_file_ops;
3237c478bdstevel@tonic-gate
3247c478bdstevel@tonic-gate	io->io_data = fdp;
3257c478bdstevel@tonic-gate	io->io_next = NULL;
3267c478bdstevel@tonic-gate	io->io_refcnt = 0;
3277c478bdstevel@tonic-gate
3287c478bdstevel@tonic-gate	return (io);
3297c478bdstevel@tonic-gate}
3307c478bdstevel@tonic-gate
3317c478bdstevel@tonic-gatemdb_io_t *
3327c478bdstevel@tonic-gatemdb_fdio_create_named(int fd, const char *name)
3337c478bdstevel@tonic-gate{
3347c478bdstevel@tonic-gate	mdb_io_t *io = mdb_fdio_create(fd);
3357c478bdstevel@tonic-gate	fd_data_t *fdp = io->io_data;
3367c478bdstevel@tonic-gate
3377c478bdstevel@tonic-gate	(void) strncpy(fdp->fd_name, name, MAXPATHLEN);
3387c478bdstevel@tonic-gate	fdp->fd_name[MAXPATHLEN - 1] = '\0';
3397c478bdstevel@tonic-gate
3407c478bdstevel@tonic-gate	return (io);
3417c478bdstevel@tonic-gate}
342a576ab5rab
343a576ab5rabint
344a576ab5rabmdb_fdio_fileno(mdb_io_t *io)
345a576ab5rab{
346a576ab5rab	fd_data_t *fdp = io->io_data;
347a576ab5rab	return (fdp->fd_fd);
348a576ab5rab}
349