xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_fdio.c (revision a576ab5b6e08c47732b3dedca9eaa8a8cbb85720)
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 
51 typedef struct fd_data {
52 	char fd_name[MAXPATHLEN];	/* Save filename for error messages */
53 	int fd_fd;			/* File descriptor */
54 } fd_data_t;
55 
56 static ssize_t
57 fdio_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 
67 static ssize_t
68 fdio_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 
78 static off64_t
79 fdio_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 
89 static int
90 fdio_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 
103 static void
104 fdio_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 
112 static const char *
113 fdio_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 
123 mdb_io_t *
124 mdb_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 
155 static 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  */
177 static ssize_t
178 fdio_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  */
218 static ssize_t
219 fdio_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 
257 static 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 
271 mdb_io_t *
272 mdb_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 
315 mdb_io_t *
316 mdb_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 
327 int
328 mdb_fdio_fileno(mdb_io_t *io)
329 {
330 	fd_data_t *fdp = io->io_data;
331 	return (fdp->fd_fd);
332 }
333