xref: /illumos-gate/usr/src/cmd/mdb/common/mdb/mdb_fdio.c (revision b55a30d7)
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 
49 typedef struct fd_data {
50 	char fd_name[MAXPATHLEN];	/* Save filename for error messages */
51 	int fd_fd;			/* File descriptor */
52 } fd_data_t;
53 
54 static ssize_t
fdio_read(mdb_io_t * io,void * buf,size_t nbytes)55 fdio_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 
65 static ssize_t
fdio_write(mdb_io_t * io,const void * buf,size_t nbytes)66 fdio_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 
76 static off64_t
fdio_seek(mdb_io_t * io,off64_t offset,int whence)77 fdio_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 
87 static int
fdio_ctl(mdb_io_t * io,int req,void * arg)88 fdio_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 
101 static void
fdio_close(mdb_io_t * io)102 fdio_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 
110 static const char *
fdio_name(mdb_io_t * io)111 fdio_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 
121 mdb_io_t *
mdb_fdio_create_path(const char * path[],const char * fname,int flags,mode_t mode)122 mdb_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 
153 static 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  */
170 static uint_t
fdio_bdev_info(int fd)171 fdio_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  */
187 static ssize_t
fdio_bdev_read(mdb_io_t * io,void * buf,size_t nbytes)188 fdio_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  */
231 static ssize_t
fdio_bdev_write(mdb_io_t * io,const void * buf,size_t nbytes)232 fdio_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 
273 static 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 
287 mdb_io_t *
mdb_fdio_create(int fd)288 mdb_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 
331 mdb_io_t *
mdb_fdio_create_named(int fd,const char * name)332 mdb_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 
343 int
mdb_fdio_fileno(mdb_io_t * io)344 mdb_fdio_fileno(mdb_io_t *io)
345 {
346 	fd_data_t *fdp = io->io_data;
347 	return (fdp->fd_fd);
348 }
349