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
fdio_read(mdb_io_t * io,void * buf,size_t nbytes)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
fdio_write(mdb_io_t * io,const void * buf,size_t nbytes)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
fdio_seek(mdb_io_t * io,off64_t offset,int whence)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
fdio_ctl(mdb_io_t * io,int req,void * arg)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
fdio_close(mdb_io_t * io)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 *
fdio_name(mdb_io_t * io)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 *
mdb_fdio_create_path(const char * path[],const char * fname,int flags,mode_t mode)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
fdio_bdev_read(mdb_io_t * io,void * buf,size_t nbytes)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
fdio_bdev_write(mdb_io_t * io,const void * buf,size_t nbytes)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 *
mdb_fdio_create(int fd)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 *
mdb_fdio_create_named(int fd,const char * name)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
mdb_fdio_fileno(mdb_io_t * io)328 mdb_fdio_fileno(mdb_io_t *io)
329 {
330 fd_data_t *fdp = io->io_data;
331 return (fdp->fd_fd);
332 }
333