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