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 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 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 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 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 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 * 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 * 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 .io_read = fdio_read, 155 .io_write = fdio_write, 156 .io_seek = fdio_seek, 157 .io_ctl = fdio_ctl, 158 .io_close = fdio_close, 159 .io_name = fdio_name, 160 .io_link = no_io_link, 161 .io_unlink = no_io_unlink, 162 .io_setattr = no_io_setattr, 163 .io_suspend = no_io_suspend, 164 .io_resume = no_io_resume 165 }; 166 167 /* 168 * Read media logical block size. On error, return DEV_BSIZE. 169 */ 170 static uint_t 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 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 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 .io_read = fdio_bdev_read, 275 .io_write = fdio_bdev_write, 276 .io_seek = fdio_seek, 277 .io_ctl = fdio_ctl, 278 .io_close = fdio_close, 279 .io_name = fdio_name, 280 .io_link = no_io_link, 281 .io_unlink = no_io_unlink, 282 .io_setattr = no_io_setattr, 283 .io_suspend = no_io_suspend, 284 .io_resume = no_io_resume, 285 }; 286 287 mdb_io_t * 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 * 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 344 mdb_fdio_fileno(mdb_io_t *io) 345 { 346 fd_data_t *fdp = io->io_data; 347 return (fdp->fd_fd); 348 } 349