1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21/*
22 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
23 * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*
29 * File Descriptor I/O Backend
30 *
31 * Simple backend to pass though io_ops to the corresponding system calls on
32 * an underlying fd.  We provide functions to create fdio objects using file
33 * descriptors, explicit file names, and path lookups.  We save the complete
34 * filename so that mdb_iob_name can be used to report the complete filename
35 * of an open macro file in syntax error messages.
36 */
37
38#include <sys/param.h>
39#include <sys/stat.h>
40#include <sys/dkio.h>
41#include <unistd.h>
42#include <string.h>
43#include <errno.h>
44#include <fcntl.h>
45
46#include <mdb/mdb_modapi.h>
47#include <mdb/mdb_err.h>
48#include <mdb/mdb_io_impl.h>
49#include <mdb/mdb.h>
50
51typedef struct fd_data {
52	char fd_name[MAXPATHLEN];	/* Save filename for error messages */
53	int fd_fd;			/* File descriptor */
54} fd_data_t;
55
56static ssize_t
57fdio_read(mdb_io_t *io, void *buf, size_t nbytes)
58{
59	fd_data_t *fdp = io->io_data;
60
61	if (io->io_next == NULL)
62		return (read(fdp->fd_fd, buf, nbytes));
63
64	return (IOP_READ(io->io_next, buf, nbytes));
65}
66
67static ssize_t
68fdio_write(mdb_io_t *io, const void *buf, size_t nbytes)
69{
70	fd_data_t *fdp = io->io_data;
71
72	if (io->io_next == NULL)
73		return (write(fdp->fd_fd, buf, nbytes));
74
75	return (IOP_WRITE(io->io_next, buf, nbytes));
76}
77
78static off64_t
79fdio_seek(mdb_io_t *io, off64_t offset, int whence)
80{
81	fd_data_t *fdp = io->io_data;
82
83	if (io->io_next == NULL)
84		return (lseek64(fdp->fd_fd, offset, whence));
85
86	return (IOP_SEEK(io->io_next, offset, whence));
87}
88
89static int
90fdio_ctl(mdb_io_t *io, int req, void *arg)
91{
92	fd_data_t *fdp = io->io_data;
93
94	if (io->io_next != NULL)
95		return (IOP_CTL(io->io_next, req, arg));
96
97	if (req == MDB_IOC_GETFD)
98		return (fdp->fd_fd);
99	else
100		return (ioctl(fdp->fd_fd, req, arg));
101}
102
103static void
104fdio_close(mdb_io_t *io)
105{
106	fd_data_t *fdp = io->io_data;
107
108	(void) close(fdp->fd_fd);
109	mdb_free(fdp, sizeof (fd_data_t));
110}
111
112static const char *
113fdio_name(mdb_io_t *io)
114{
115	fd_data_t *fdp = io->io_data;
116
117	if (io->io_next == NULL)
118		return (fdp->fd_name);
119
120	return (IOP_NAME(io->io_next));
121}
122
123mdb_io_t *
124mdb_fdio_create_path(const char *path[], const char *fname,
125    int flags, mode_t mode)
126{
127	int fd;
128	char buf[MAXPATHLEN];
129
130	if (path != NULL && strchr(fname, '/') == NULL) {
131		int i;
132
133		for (fd = -1, i = 0; path[i] != NULL; i++) {
134			(void) mdb_iob_snprintf(buf, MAXPATHLEN, "%s/%s",
135			    path[i], fname);
136
137			if (access(buf, F_OK) == 0) {
138				fd = open64(buf, flags, mode);
139				fname = buf;
140				break;
141			}
142		}
143
144		if (fd == -1)
145			(void) set_errno(ENOENT);
146	} else
147		fd = open64(fname, flags, mode);
148
149	if (fd >= 0)
150		return (mdb_fdio_create_named(fd, fname));
151
152	return (NULL);
153}
154
155static const mdb_io_ops_t fdio_file_ops = {
156	fdio_read,
157	fdio_write,
158	fdio_seek,
159	fdio_ctl,
160	fdio_close,
161	fdio_name,
162	no_io_link,
163	no_io_unlink,
164	no_io_setattr,
165	no_io_suspend,
166	no_io_resume
167};
168
169/*
170 * In order to read from a block-oriented device, we pick up the seek pointer,
171 * read each containing block, and then copy the desired range of bytes back
172 * into the caller's buffer.  Unfortunately Solaris hardcodes the notion of
173 * DEV_BSIZE as the transfer unit for such devices; no ioctl() to obtain the
174 * transfer unit dynamically is currently available.  At the end of the
175 * transfer we reset the seek pointer to where the caller thinks it should be.
176 */
177static ssize_t
178fdio_bdev_read(mdb_io_t *io, void *buf, size_t nbytes)
179{
180	fd_data_t *fdp = io->io_data;
181	ssize_t resid = nbytes;
182	uchar_t blk[DEV_BSIZE];
183	off64_t off;
184
185	if (io->io_next != NULL)
186		return (IOP_READ(io->io_next, buf, nbytes));
187
188	if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
189		return (-1); /* errno is set for us */
190
191	while (resid != 0) {
192		off64_t devoff = off & ~(DEV_BSIZE - 1);
193		size_t blkoff = off & (DEV_BSIZE - 1);
194		size_t len = MIN(resid, DEV_BSIZE - blkoff);
195
196		if (pread64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
197			break; /* errno is set for us, unless EOF */
198
199		bcopy(&blk[blkoff], buf, len);
200		resid -= len;
201		off += len;
202		buf = (char *)buf + len;
203	}
204
205	if (resid == nbytes && nbytes != 0)
206		return (set_errno(EMDB_EOF));
207
208	(void) lseek64(fdp->fd_fd, off, SEEK_SET);
209	return (nbytes - resid);
210}
211
212/*
213 * To perform a write to a block-oriented device, we use the same basic
214 * algorithm as fdio_bdev_read(), above.  In the inner loop, we read an
215 * entire block, modify it using the data from the caller's buffer, and
216 * then write the entire block back to the device.
217 */
218static ssize_t
219fdio_bdev_write(mdb_io_t *io, const void *buf, size_t nbytes)
220{
221	fd_data_t *fdp = io->io_data;
222	ssize_t resid = nbytes;
223	uchar_t blk[DEV_BSIZE];
224	off64_t off;
225
226	if (io->io_next != NULL)
227		return (IOP_WRITE(io->io_next, buf, nbytes));
228
229	if ((off = lseek64(fdp->fd_fd, 0, SEEK_CUR)) == -1)
230		return (-1); /* errno is set for us */
231
232	while (resid != 0) {
233		off64_t devoff = off & ~(DEV_BSIZE - 1);
234		size_t blkoff = off & (DEV_BSIZE - 1);
235		size_t len = MIN(resid, DEV_BSIZE - blkoff);
236
237		if (pread64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
238			break; /* errno is set for us, unless EOF */
239
240		bcopy(buf, &blk[blkoff], len);
241
242		if (pwrite64(fdp->fd_fd, blk, DEV_BSIZE, devoff) != DEV_BSIZE)
243			break; /* errno is set for us, unless EOF */
244
245		resid -= len;
246		off += len;
247		buf = (char *)buf + len;
248	}
249
250	if (resid == nbytes && nbytes != 0)
251		return (set_errno(EMDB_EOF));
252
253	(void) lseek64(fdp->fd_fd, off, SEEK_SET);
254	return (nbytes - resid);
255}
256
257static const mdb_io_ops_t fdio_bdev_ops = {
258	fdio_bdev_read,
259	fdio_bdev_write,
260	fdio_seek,
261	fdio_ctl,
262	fdio_close,
263	fdio_name,
264	no_io_link,
265	no_io_unlink,
266	no_io_setattr,
267	no_io_suspend,
268	no_io_resume
269};
270
271mdb_io_t *
272mdb_fdio_create(int fd)
273{
274	mdb_io_t *io = mdb_alloc(sizeof (mdb_io_t), UM_SLEEP);
275	fd_data_t *fdp = mdb_alloc(sizeof (fd_data_t), UM_SLEEP);
276
277	struct dk_cinfo info;
278	struct stat64 st;
279
280	switch (fd) {
281	case STDIN_FILENO:
282		(void) strcpy(fdp->fd_name, "(stdin)");
283		break;
284	case STDOUT_FILENO:
285		(void) strcpy(fdp->fd_name, "(stdout)");
286		break;
287	case STDERR_FILENO:
288		(void) strcpy(fdp->fd_name, "(stderr)");
289		break;
290	default:
291		(void) mdb_iob_snprintf(fdp->fd_name, MAXPATHLEN, "fd %d", fd);
292	}
293
294	fdp->fd_fd = fd;
295
296	/*
297	 * We determine if something is a raw block-oriented disk device by
298	 * testing to see if it is a character device that supports DKIOCINFO.
299	 * If we are operating on a disk in raw mode, we must do our own
300	 * block-oriented i/o; otherwise we can just use read() and write().
301	 */
302	if (fstat64(fd, &st) == 0 && S_ISCHR(st.st_mode) &&
303	    ioctl(fd, DKIOCINFO, &info) == 0)
304		io->io_ops = &fdio_bdev_ops;
305	else
306		io->io_ops = &fdio_file_ops;
307
308	io->io_data = fdp;
309	io->io_next = NULL;
310	io->io_refcnt = 0;
311
312	return (io);
313}
314
315mdb_io_t *
316mdb_fdio_create_named(int fd, const char *name)
317{
318	mdb_io_t *io = mdb_fdio_create(fd);
319	fd_data_t *fdp = io->io_data;
320
321	(void) strncpy(fdp->fd_name, name, MAXPATHLEN);
322	fdp->fd_name[MAXPATHLEN - 1] = '\0';
323
324	return (io);
325}
326
327int
328mdb_fdio_fileno(mdb_io_t *io)
329{
330	fd_data_t *fdp = io->io_data;
331	return (fdp->fd_fd);
332}
333