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