blob: 6fb9a7e1b09fe4bf4c20d29b40103f1924e16823 [file] [log] [blame]
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
petede7f4f06d2006-07-21 13:27:11 -07005 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07007 *
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/*
raba576ab52008-03-03 17:05:43 -080022 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070023 * 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;
petede7f4f06d2006-07-21 13:27:11 -0700128 char buf[MAXPATHLEN];
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700129
130 if (path != NULL && strchr(fname, '/') == NULL) {
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700131 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}
raba576ab52008-03-03 17:05:43 -0800326
327int
328mdb_fdio_fileno(mdb_io_t *io)
329{
330 fd_data_t *fdp = io->io_data;
331 return (fdp->fd_fd);
332}