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