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