|
|
1.1.1.3 ! root 1: /* $Id: posix-disk.c,v 1.6 2010/06/05 14:28:57 fredette Exp $ */ 1.1 root 2: 3: /* host/posix/posix-disk.c - implementation of disks on a POSIX system: */ 4: 5: /* 6: * Copyright (c) 2003 Matt Fredette 7: * All rights reserved. 8: * 9: * Redistribution and use in source and binary forms, with or without 10: * modification, are permitted provided that the following conditions 11: * are met: 12: * 1. Redistributions of source code must retain the above copyright 13: * notice, this list of conditions and the following disclaimer. 14: * 2. Redistributions in binary form must reproduce the above copyright 15: * notice, this list of conditions and the following disclaimer in the 16: * documentation and/or other materials provided with the distribution. 17: * 3. All advertising materials mentioning features or use of this software 18: * must display the following acknowledgement: 19: * This product includes software developed by Matt Fredette. 20: * 4. The name of the author may not be used to endorse or promote products 21: * derived from this software without specific prior written permission. 22: * 23: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 24: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 25: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 27: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 28: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 31: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 32: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 33: * POSSIBILITY OF SUCH DAMAGE. 34: */ 35: 1.1.1.3 ! root 36: /* this might enable large-file support: */ ! 37: #define _FILE_OFFSET_BITS 64 ! 38: 1.1 root 39: #include <tme/common.h> 1.1.1.3 ! root 40: _TME_RCSID("$Id: posix-disk.c,v 1.6 2010/06/05 14:28:57 fredette Exp $"); 1.1 root 41: 42: /* includes: */ 43: #include <tme/generic/disk.h> 44: #include <tme/generic/bus.h> 45: #include <fcntl.h> 46: #include <stdlib.h> 47: #include <strings.h> 48: #include <sys/stat.h> 49: #include <sys/uio.h> 50: #ifdef HAVE_MMAP 51: #include <sys/types.h> 52: #include <sys/mman.h> 53: #endif /* HAVE_MMAP */ 54: #ifdef HAVE_STDARG_H 55: #include <stdarg.h> 56: #else /* HAVE_STDARG_H */ 57: #include <varargs.h> 58: #endif /* HAVE_STDARG_H */ 59: 60: /* macros: */ 61: 62: /* the maximum block size: */ 63: #define TME_POSIX_DISK_BLOCK_SIZE_MAX (16384) 64: 65: /* disk flags: */ 66: #define TME_POSIX_DISK_FLAG_RO TME_BIT(0) 67: 68: /* buffer flags: */ 69: #define TME_POSIX_DISK_BUFFER_READABLE TME_BIT(0) 70: #define TME_POSIX_DISK_BUFFER_DIRTY TME_BIT(1) 71: #define TME_POSIX_DISK_BUFFER_MMAPPED TME_BIT(2) 72: 73: /* default buffer parameters: */ 74: #define TME_POSIX_DISK_BUFFER_DEFAULT_COUNT (16) 75: #define TME_POSIX_DISK_BUFFER_DEFAULT_AGG_PRE (128UL * 1024UL) 76: #define TME_POSIX_DISK_BUFFER_DEFAULT_AGG_POST (1UL * 1024UL * 1024UL) 77: 78: /* types: */ 79: 80: /* a posix disk buffer: */ 81: struct tme_posix_disk_buffer { 82: 83: /* buffers are kept on a doubly linked list: */ 84: struct tme_posix_disk_buffer *tme_posix_disk_buffer_next; 85: struct tme_posix_disk_buffer **tme_posix_disk_buffer_prev; 86: 87: /* buffer flags: */ 88: int tme_posix_disk_buffer_flags; 89: 90: /* the file position, size, and data of this buffer. a size of zero 91: means this buffer is free: */ 92: 93: /* we're paranoid about autoconf's ability to actually determine if 94: off_t and size_t are available, and it's possible that a system 95: that predates off_t and size_t may use other types to achieve 96: large file support anyways, that autoconf's default off_t = long 97: and size_t = unsigned definitions may not match. the bizarre 98: solution is to declare structs with appropriately-typed members, 99: and then use those members: */ 100: struct stat _tme_posix_disk_buffer_stat; 101: struct iovec _tme_posix_disk_buffer_iov; 102: #define tme_posix_disk_buffer_pos _tme_posix_disk_buffer_stat.st_size 103: #define tme_posix_disk_buffer_size _tme_posix_disk_buffer_iov.iov_len 104: #define tme_posix_disk_buffer_data _tme_posix_disk_buffer_iov.iov_base 105: }; 106: 107: /* a posix disk: */ 108: struct tme_posix_disk { 109: 110: /* backpointer to our element: */ 111: struct tme_element *tme_posix_disk_element; 112: 113: /* our mutex: */ 114: tme_mutex_t tme_posix_disk_mutex; 115: 116: /* our flags: */ 117: int tme_posix_disk_flags; 118: 119: /* the file descriptor: */ 120: int tme_posix_disk_fd; 121: 122: /* the stat buffer: */ 123: struct stat tme_posix_disk_stat; 124: 125: /* our connection: */ 126: struct tme_disk_connection *tme_posix_disk_connection; 127: 128: /* our disk buffers. the most-recently-used buffer 129: is at the front of the list: */ 130: struct tme_posix_disk_buffer *tme_posix_disk_buffers; 131: 132: /* how much we aggregate behind and ahead when we will 133: a new buffer: */ 134: struct stat _tme_posix_disk_stat0; 135: struct stat _tme_posix_disk_stat1; 136: #define tme_posix_disk_buffer_agg_pre _tme_posix_disk_stat0.st_size 137: #define tme_posix_disk_buffer_agg_post _tme_posix_disk_stat1.st_size 138: }; 139: 1.1.1.3 ! root 140: /* this frees a buffer: */ ! 141: static void ! 142: _tme_posix_disk_buffer_free(struct tme_posix_disk *posix_disk, ! 143: struct tme_posix_disk_buffer *buffer) ! 144: { ! 145: int rc; ! 146: struct iovec iovecs[1]; ! 147: #define ssize iovecs[0].iov_len ! 148: ! 149: /* if this buffer is mmapped: */ ! 150: if (buffer->tme_posix_disk_buffer_flags ! 151: & TME_POSIX_DISK_BUFFER_MMAPPED) { ! 152: #ifdef HAVE_MMAP ! 153: ! 154: /* munmap the buffer: */ ! 155: rc = munmap(buffer->tme_posix_disk_buffer_data, ! 156: buffer->tme_posix_disk_buffer_size); ! 157: assert (rc == 0); ! 158: ! 159: /* free this buffer: */ ! 160: buffer->tme_posix_disk_buffer_size = 0; ! 161: #endif /* HAVE_MMAP */ ! 162: } ! 163: ! 164: /* otherwise, this buffer is not mmapped: */ ! 165: else { ! 166: ! 167: /* if this buffer is dirty, we need to write it out: */ ! 168: if (buffer->tme_posix_disk_buffer_flags ! 169: & TME_POSIX_DISK_BUFFER_DIRTY) { ! 170: ! 171: /* seek to the buffer's position: */ ! 172: rc = (lseek(posix_disk->tme_posix_disk_fd, ! 173: buffer->tme_posix_disk_buffer_pos, ! 174: SEEK_SET) < 0); ! 175: assert (rc == 0); ! 176: ! 177: /* write out the buffer: */ ! 178: ssize = write(posix_disk->tme_posix_disk_fd, ! 179: buffer->tme_posix_disk_buffer_data, ! 180: buffer->tme_posix_disk_buffer_size); ! 181: assert (ssize == buffer->tme_posix_disk_buffer_size); ! 182: } ! 183: ! 184: /* free this buffer: */ ! 185: buffer->tme_posix_disk_buffer_flags = 0; ! 186: } ! 187: #undef ssize ! 188: } ! 189: 1.1 root 190: /* this gets a buffer: */ 191: static int 192: _tme_posix_disk_buffer_get(struct tme_posix_disk *posix_disk, 193: const union tme_value64 *_pos, 194: unsigned long _size, 195: int readable, 196: tme_uint8_t **_buffer) 197: { 198: struct stat statbufs[3]; 199: #define pos_least statbufs[0].st_size 200: #define pos_most statbufs[1].st_size 201: #define pos_last statbufs[2].st_size 202: struct iovec iovecs[3]; 203: #define data iovecs[0].iov_base 204: #define size iovecs[0].iov_len 205: #define size_agg iovecs[1].iov_len 206: #define ssize iovecs[2].iov_len 207: unsigned long agg_pre, agg_post; 208: struct tme_posix_disk_buffer *buffer; 209: struct tme_posix_disk_buffer *buffer_free_nosize; 210: struct tme_posix_disk_buffer *buffer_free_sized; 211: int have_least, have_most; 212: int rc; 213: 214: /* form the size, and least and most positions: */ 215: size = _size; 216: assert (size > 0); 217: #ifdef TME_HAVE_INT64_T 218: pos_least = _pos->tme_value64_int; 219: #else /* !TME_HAVE_INT64_T */ 220: pos_least = _pos->tme_value64_int32_hi; 221: pos_least 222: = ((pos_least << 32) 223: | _pos->tme_value64_int32_lo); 224: #endif /* !TME_HAVE_INT64_T */ 225: pos_most = (pos_least + size) - 1; 226: 227: /* if we have to fill a new buffer, decide from where and how much 228: we are going to fill: */ 229: agg_pre 230: = TME_MIN(pos_least, 231: posix_disk->tme_posix_disk_buffer_agg_pre); 232: agg_pre 233: += ((pos_least - agg_pre) 234: & (posix_disk->tme_posix_disk_stat.st_blksize - 1)); 235: agg_post 236: = posix_disk->tme_posix_disk_buffer_agg_post; 237: size_agg 238: = (((agg_pre + size) + agg_post 239: + (posix_disk->tme_posix_disk_stat.st_blksize - 1)) 240: & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1)); 241: agg_post = (size_agg - (agg_pre + size)); 242: 243: /* start with no best free buffers: */ 244: buffer_free_nosize = NULL; 245: buffer_free_sized = NULL; 246: 247: /* walk all of the buffers: */ 248: for (buffer = posix_disk->tme_posix_disk_buffers; 249: buffer != NULL; ) { 250: 251: /* a buffer with no size is free: */ 252: if (buffer->tme_posix_disk_buffer_size == 0) { 253: 254: /* remember this nosize free buffer and continue: */ 255: buffer_free_nosize = buffer; 256: buffer = buffer->tme_posix_disk_buffer_next; 257: continue; 258: } 259: 260: /* a buffer with some size, but no flags, is also free: */ 261: else if (buffer->tme_posix_disk_buffer_flags == 0) { 262: 263: /* take this buffer as our best sized free buffer, */ 264: 265: /* if we have no best sized free buffer yet: */ 266: if (buffer_free_sized == NULL 267: 268: || ((buffer->tme_posix_disk_buffer_size 269: >= buffer_free_sized->tme_posix_disk_buffer_size) 270: 271: /* if this buffer is bigger than our best sized free 272: buffer, and the best sized free buffer is smaller 273: than the new-fill aggregate buffer size: */ 274: ? (buffer_free_sized->tme_posix_disk_buffer_size 275: < size_agg) 276: 277: /* if this buffer is smaller than our best sized 278: free buffer, but it is still at least as big as 279: the new-fill aggregate buffer size: */ 280: : (buffer->tme_posix_disk_buffer_size 281: >= size_agg))) { 282: buffer_free_sized = buffer; 283: } 284: 285: /* continue: */ 286: buffer = buffer->tme_posix_disk_buffer_next; 287: continue; 288: } 289: 290: /* calculate the last position in this buffer: */ 291: pos_last = ((buffer->tme_posix_disk_buffer_pos 292: + buffer->tme_posix_disk_buffer_size) 293: - 1); 294: 295: /* see if this buffer contains the least position we want: */ 296: have_least 297: = (buffer->tme_posix_disk_buffer_pos <= pos_least 298: && pos_least <= pos_last); 299: 300: /* see if this buffer contains the most position we want: */ 301: have_most 302: = (buffer->tme_posix_disk_buffer_pos <= pos_most 303: && pos_most <= pos_last); 304: 305: /* if this buffer covers all of the positions we want, and 306: is readable or we don't need a readable buffer, stop: */ 307: if (have_least 308: && have_most 309: && ((buffer->tme_posix_disk_buffer_flags 310: & TME_POSIX_DISK_BUFFER_READABLE) 311: || !readable)) { 312: break; 313: } 314: 315: /* otherwise, if this buffer covers any of the positions we want, 316: or if this is the last buffer on the list and we don't have any 317: best free buffer, we need to free this buffer: */ 318: else if (have_least 319: || have_most 320: || (buffer->tme_posix_disk_buffer_next == NULL 321: && buffer_free_nosize == NULL 322: && buffer_free_sized == NULL)) { 323: 1.1.1.3 ! root 324: /* free this buffer: */ ! 325: _tme_posix_disk_buffer_free(posix_disk, ! 326: buffer); 1.1 root 327: 328: /* continue without updating buffer, so that this now-free 329: buffer is revisited, to possibly become a best free buffer: */ 330: } 331: 332: /* otherwise, this is a non-free buffer that doesn't 333: cover any of the positions we want. just continue: */ 334: else { 335: buffer = buffer->tme_posix_disk_buffer_next; 336: } 337: } 338: 339: /* if we didn't find an applicable buffer: */ 340: if (buffer == NULL) { 341: 342: /* we must have some free buffer: */ 343: assert (buffer_free_nosize != NULL 344: || buffer_free_sized != NULL); 345: 346: #ifdef HAVE_MMAP 347: 348: /* try to mmap this region. if the map fails with more read-ahead 349: than is needed to meet the block size, try the map one more 350: time with just that needed read-ahead: */ 351: data = mmap(NULL, 352: size_agg, 353: PROT_READ 354: | ((posix_disk->tme_posix_disk_flags 355: & TME_POSIX_DISK_FLAG_RO) 356: ? 0 357: : PROT_WRITE), 358: MAP_SHARED, 359: posix_disk->tme_posix_disk_fd, 360: (pos_least 361: - agg_pre)); 362: if (data == MAP_FAILED) { 363: size_agg 364: = (((agg_pre + size) 365: + (posix_disk->tme_posix_disk_stat.st_blksize - 1)) 366: & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1)); 367: data = mmap(NULL, 368: size_agg, 369: PROT_READ 370: | ((posix_disk->tme_posix_disk_flags 371: & TME_POSIX_DISK_FLAG_RO) 372: ? 0 373: : PROT_WRITE), 374: MAP_SHARED, 375: posix_disk->tme_posix_disk_fd, 376: (pos_least 377: - agg_pre)); 378: if (data == MAP_FAILED) { 379: size_agg = (agg_pre + size) + agg_post; 380: } 381: else { 382: agg_post = size_agg - (agg_pre + size); 383: } 384: } 385: 386: /* if we were able to mmap this region: */ 387: if (data != MAP_FAILED) { 388: 389: /* if we have a free nosize buffer, reuse it, else free the 390: free sized buffer's data and reuse that: */ 391: if (buffer_free_nosize != NULL) { 392: buffer = buffer_free_nosize; 393: } 394: else { 395: tme_free(buffer_free_sized->tme_posix_disk_buffer_data); 396: buffer = buffer_free_sized; 397: } 398: 399: /* do the mmapped-specific initialization of this buffer: */ 400: buffer->tme_posix_disk_buffer_flags 401: = (TME_POSIX_DISK_BUFFER_READABLE 402: | TME_POSIX_DISK_BUFFER_MMAPPED); 403: } 404: 405: /* otherwise, we were unable to map this region: */ 406: else 407: #endif /* HAVE_MMAP */ 408: { 409: 410: /* if we have a free sized buffer, resize it, else malloc 411: data for the free nosize buffer: */ 412: if (buffer_free_sized != NULL) { 413: data = buffer_free_sized->tme_posix_disk_buffer_data; 414: if (buffer_free_sized->tme_posix_disk_buffer_size 415: != size_agg) { 416: data = tme_realloc(data, size_agg); 417: } 418: buffer = buffer_free_sized; 419: } 420: else { 421: data = tme_malloc(size_agg); 422: buffer = buffer_free_nosize; 423: } 424: 425: /* do the malloced-specific initialization of this buffer: */ 426: buffer->tme_posix_disk_buffer_flags = 0; 427: if (readable) { 428: buffer->tme_posix_disk_buffer_flags 429: = TME_POSIX_DISK_BUFFER_READABLE; 430: 431: /* seek to the buffer's position: */ 432: rc = (lseek(posix_disk->tme_posix_disk_fd, 433: (pos_least 434: - agg_pre), 435: SEEK_SET) < 0); 436: assert (rc == 0); 437: 438: /* read in the buffer. if the read fails with more 439: read-ahead than is needed to meet the block size, try the 440: read one more time with just that needed read-ahead: */ 441: for (;;) { 442: ssize = read(posix_disk->tme_posix_disk_fd, 443: data, 444: size_agg); 445: if (ssize == size_agg) { 446: break; 447: } 448: size_agg 449: = (((agg_pre + size) 450: + (posix_disk->tme_posix_disk_stat.st_blksize - 1)) 451: & ~(posix_disk->tme_posix_disk_stat.st_blksize - 1)); 452: assert (agg_post > (size_agg - (agg_pre + size))); 453: agg_post = (size_agg - (agg_pre + size)); 454: } 455: } 456: } 457: 458: /* do the common initialization of this buffer: */ 459: buffer->tme_posix_disk_buffer_pos 460: = (pos_least 461: - agg_pre); 462: buffer->tme_posix_disk_buffer_size 463: = size_agg; 464: buffer->tme_posix_disk_buffer_data 465: = data; 466: } 467: 468: /* if this buffer doesn't need to be readable, that means that it's 469: being written to, so mark it dirty: */ 470: if (!readable) { 471: buffer->tme_posix_disk_buffer_flags 472: |= TME_POSIX_DISK_BUFFER_DIRTY; 473: } 474: 475: /* remove this buffer from the list: */ 476: *buffer->tme_posix_disk_buffer_prev 477: = buffer->tme_posix_disk_buffer_next; 478: if (buffer->tme_posix_disk_buffer_next != NULL) { 479: buffer->tme_posix_disk_buffer_next->tme_posix_disk_buffer_prev 480: = buffer->tme_posix_disk_buffer_prev; 481: } 482: 483: /* add this buffer to the front of the list: */ 484: buffer->tme_posix_disk_buffer_prev 485: = &posix_disk->tme_posix_disk_buffers; 486: buffer->tme_posix_disk_buffer_next 487: = posix_disk->tme_posix_disk_buffers; 488: if (buffer->tme_posix_disk_buffer_next != NULL) { 489: buffer->tme_posix_disk_buffer_next->tme_posix_disk_buffer_prev 490: = &buffer->tme_posix_disk_buffer_next; 491: } 492: *buffer->tme_posix_disk_buffer_prev 493: = buffer; 494: 495: /* return the desired pointer into the buffer: */ 496: *_buffer 497: = (buffer->tme_posix_disk_buffer_data 498: + (pos_least 499: - buffer->tme_posix_disk_buffer_pos)); 500: 501: return (TME_OK); 502: #undef pos_least 503: #undef pos_most 504: #undef data 505: #undef size 506: #undef size_agg 507: } 508: 509: /* this returns a read buffer: */ 510: static int 511: _tme_posix_disk_read(struct tme_disk_connection *conn_disk, 512: const union tme_value64 *pos, 513: unsigned long size, 514: const tme_uint8_t **_buffer) 515: { 516: struct tme_posix_disk *posix_disk; 517: tme_uint8_t *buffer; 518: int rc; 519: 520: /* recover our data structure: */ 521: posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private; 522: 523: /* lock the mutex: */ 524: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex); 525: 526: /* get the buffer: */ 527: rc = _tme_posix_disk_buffer_get(posix_disk, 528: pos, 529: size, 530: TRUE, 531: &buffer); 532: assert (rc == TME_OK); 533: 534: /* unlock the mutex: */ 535: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex); 536: 537: /* return the buffer: */ 538: *_buffer = buffer; 539: return (TME_OK); 540: } 541: 542: /* this returns a write buffer: */ 543: static int 544: _tme_posix_disk_write(struct tme_disk_connection *conn_disk, 545: const union tme_value64 *pos, 546: unsigned long size, 547: tme_uint8_t **_buffer) 548: { 549: struct tme_posix_disk *posix_disk; 550: int rc; 551: 552: /* recover our data structure: */ 553: posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private; 554: 555: /* lock the mutex: */ 556: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex); 557: 558: /* get the buffer: */ 559: rc = _tme_posix_disk_buffer_get(posix_disk, 560: pos, 561: size, 562: FALSE, 563: _buffer); 564: assert (rc == TME_OK); 565: 566: /* unlock the mutex: */ 567: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex); 568: 569: return (TME_OK); 570: } 571: 572: /* the disk control handler: */ 573: #ifdef HAVE_STDARG_H 574: static int _tme_posix_disk_control(struct tme_disk_connection *conn_disk, 575: unsigned int control, 576: ...) 577: #else /* HAVE_STDARG_H */ 578: static int _tme_posix_disk_control(conn_disk, control, va_alist) 579: struct tme_disk_connection *conn_disk; 580: unsigned int control; 581: va_dcl 582: #endif /* HAVE_STDARG_H */ 583: { 584: struct tme_posix_disk *posix_disk; 585: 586: /* recover our data structure: */ 587: posix_disk = (struct tme_posix_disk *) conn_disk->tme_disk_connection.tme_connection_element->tme_element_private; 588: 589: /* lock the mutex: */ 590: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex); 591: 592: /* unlock the mutex: */ 593: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex); 594: 595: return (TME_OK); 596: } 597: 1.1.1.3 ! root 598: /* this opens a disk: */ ! 599: static int ! 600: _tme_posix_disk_open(struct tme_posix_disk *posix_disk, ! 601: const char *filename, ! 602: int flags, ! 603: char **_output) ! 604: { ! 605: int fd; ! 606: struct stat statbuf; ! 607: tme_uint8_t *block; ! 608: #ifdef HAVE_MMAP ! 609: int page_size; ! 610: #endif /* HAVE_MMAP */ ! 611: ! 612: /* open the file: */ ! 613: fd = open(filename, ! 614: ((flags ! 615: & TME_POSIX_DISK_FLAG_RO) ! 616: ? O_RDONLY ! 617: : O_RDWR)); ! 618: if (fd < 0) { ! 619: tme_output_append_error(_output, ! 620: "%s", ! 621: filename); ! 622: return (errno); ! 623: } ! 624: ! 625: /* stat the file: */ ! 626: if (fstat(fd, &statbuf) < 0) { ! 627: tme_output_append_error(_output, ! 628: "%s", ! 629: filename); ! 630: close(fd); ! 631: return (errno); ! 632: } ! 633: ! 634: /* we will handle a character device, but not a block device: */ ! 635: if (S_ISBLK(statbuf.st_mode)) { ! 636: tme_output_append_error(_output, ! 637: "%s", ! 638: filename); ! 639: close(fd); ! 640: return (EINVAL); ! 641: } ! 642: ! 643: /* if this is a character device, determine its block size: */ ! 644: if (S_ISCHR(statbuf.st_mode)) { ! 645: ! 646: /* the block size must be at least one: */ ! 647: statbuf.st_blksize = 1; ! 648: ! 649: /* allocate space for the block: */ ! 650: block = tme_new(tme_uint8_t, statbuf.st_blksize); ! 651: ! 652: /* loop trying to read a block at offset zero, doubling the block ! 653: size until we succeed: */ ! 654: for (; statbuf.st_blksize <= TME_POSIX_DISK_BLOCK_SIZE_MAX; ) { ! 655: ! 656: /* do the read: */ ! 657: if (read(fd, block, statbuf.st_blksize) >= 0) { ! 658: break; ! 659: } ! 660: ! 661: /* seek back to the beginning: */ ! 662: if (lseek(fd, 0, SEEK_SET) < 0) { ! 663: tme_free(block); ! 664: tme_output_append_error(_output, ! 665: "%s", ! 666: filename); ! 667: close(fd); ! 668: return (errno); ! 669: } ! 670: ! 671: /* resize the block: */ ! 672: statbuf.st_blksize <<= 1; ! 673: block = tme_renew(tme_uint8_t, block, statbuf.st_blksize); ! 674: } ! 675: ! 676: /* free the block: */ ! 677: tme_free(block); ! 678: ! 679: /* if we failed: */ ! 680: if (statbuf.st_blksize > TME_POSIX_DISK_BLOCK_SIZE_MAX) { ! 681: tme_output_append_error(_output, ! 682: "%s", ! 683: filename); ! 684: close(fd); ! 685: return (EINVAL); ! 686: } ! 687: } ! 688: ! 689: #ifdef HAVE_MMAP ! 690: /* if we're mmapping, the block size must be at least the page size: */ ! 691: for (page_size = getpagesize(); ! 692: page_size < statbuf.st_blksize; ! 693: page_size <<= 1); ! 694: statbuf.st_blksize = page_size; ! 695: #endif /* HAVE_MMAP */ ! 696: ! 697: /* update the disk structure: */ ! 698: posix_disk->tme_posix_disk_flags = flags; ! 699: posix_disk->tme_posix_disk_fd = fd; ! 700: posix_disk->tme_posix_disk_stat = statbuf; ! 701: return (TME_OK); ! 702: } ! 703: ! 704: /* this closes a disk: */ ! 705: static void ! 706: _tme_posix_disk_close(struct tme_posix_disk *posix_disk) ! 707: { ! 708: struct tme_posix_disk_buffer *buffer; ! 709: ! 710: /* free all of the buffers: */ ! 711: for (buffer = posix_disk->tme_posix_disk_buffers; ! 712: buffer != NULL; ! 713: buffer = buffer->tme_posix_disk_buffer_next) { ! 714: _tme_posix_disk_buffer_free(posix_disk, ! 715: buffer); ! 716: } ! 717: ! 718: /* close the disk: */ ! 719: close(posix_disk->tme_posix_disk_fd); ! 720: posix_disk->tme_posix_disk_fd = -1; ! 721: } ! 722: ! 723: /* our internal command function: */ ! 724: static int ! 725: __tme_posix_disk_command(struct tme_posix_disk *posix_disk, ! 726: const char * const * args, ! 727: char **_output) ! 728: { ! 729: int usage; ! 730: int arg_i; ! 731: const char *filename; ! 732: int flags; ! 733: int rc; ! 734: ! 735: /* check the command: */ ! 736: usage = FALSE; ! 737: arg_i = 1; ! 738: ! 739: /* the "load" command: */ ! 740: if (TME_ARG_IS(args[arg_i], "load")) { ! 741: arg_i++; ! 742: ! 743: /* if a disk is currently loaded, it must be unloaded first: */ ! 744: if (posix_disk->tme_posix_disk_fd >= 0) { ! 745: tme_output_append_error(_output, ! 746: _("%s: disk already loaded; must unload first"), ! 747: args[0]); ! 748: return (EBUSY); ! 749: } ! 750: ! 751: /* the first argument is the filename: */ ! 752: filename = args[arg_i]; ! 753: arg_i += (filename != NULL); ! 754: ! 755: /* any remaining arguments are flags: */ ! 756: flags = 0; ! 757: for (;;) { ! 758: ! 759: /* the "read-only" flag: */ ! 760: if (TME_ARG_IS(args[arg_i], "read-only")) { ! 761: flags |= TME_POSIX_DISK_FLAG_RO; ! 762: arg_i++; ! 763: } ! 764: ! 765: else { ! 766: break; ! 767: } ! 768: } ! 769: ! 770: /* if we don't have a filename, or if there are more arguments: */ ! 771: if (filename == NULL ! 772: || args[arg_i] != NULL) { ! 773: ! 774: tme_output_append_error(_output, ! 775: "%s %s load { %s | %s } [read-only]", ! 776: _("usage:"), ! 777: args[0], ! 778: _("DEVICE"), ! 779: _("FILENAME")); ! 780: rc = EINVAL; ! 781: } ! 782: ! 783: /* otherwise, if we can open the disk: */ ! 784: else if ((rc = _tme_posix_disk_open(posix_disk, ! 785: filename, ! 786: flags, ! 787: _output)) == TME_OK) { ! 788: ! 789: /* nothing to do */ ! 790: } ! 791: } ! 792: ! 793: /* the "unload" command: */ ! 794: else if (TME_ARG_IS(args[arg_i], "unload")) { ! 795: ! 796: /* if no disk is currently loaded: */ ! 797: if (posix_disk->tme_posix_disk_fd < 0) { ! 798: tme_output_append_error(_output, ! 799: _("%s: no disk loaded"), ! 800: args[0]); ! 801: return (ENXIO); ! 802: } ! 803: ! 804: /* we must have no arguments: */ ! 805: if (args[arg_i + 1] != NULL) { ! 806: tme_output_append_error(_output, ! 807: "%s %s unload", ! 808: _("usage:"), ! 809: args[0]); ! 810: return (EINVAL); ! 811: } ! 812: ! 813: /* close the disk: */ ! 814: _tme_posix_disk_close(posix_disk); ! 815: rc = TME_OK; ! 816: } ! 817: ! 818: /* any other command: */ ! 819: else { ! 820: if (args[arg_i] != NULL) { ! 821: tme_output_append_error(_output, ! 822: "%s '%s', ", ! 823: _("unknown command"), ! 824: args[1]); ! 825: } ! 826: tme_output_append_error(_output, ! 827: _("available %s commands: %s"), ! 828: args[0], ! 829: "load unload"); ! 830: return (EINVAL); ! 831: } ! 832: ! 833: return (rc); ! 834: } ! 835: ! 836: /* our command function: */ ! 837: static int ! 838: _tme_posix_disk_command(struct tme_element *element, ! 839: const char * const * args, ! 840: char **_output) ! 841: { ! 842: struct tme_posix_disk *posix_disk; ! 843: int rc; ! 844: ! 845: /* recover our data structure: */ ! 846: posix_disk = (struct tme_posix_disk *) element->tme_element_private; ! 847: ! 848: /* lock the mutex: */ ! 849: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex); ! 850: ! 851: /* call the internal command function: */ ! 852: rc = __tme_posix_disk_command(posix_disk, ! 853: args, ! 854: _output); ! 855: ! 856: /* unlock the mutex: */ ! 857: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex); ! 858: ! 859: return (rc); ! 860: } ! 861: 1.1 root 862: /* this breaks a posix disk connection: */ 863: static int 864: _tme_posix_disk_connection_break(struct tme_connection *conn, 865: unsigned int state) 866: { 867: abort(); 868: } 869: 870: /* this makes a posix disk connection: */ 871: static int 872: _tme_posix_disk_connection_make(struct tme_connection *conn, 873: unsigned int state) 874: { 875: struct tme_posix_disk *posix_disk; 876: struct tme_disk_connection *conn_disk; 877: 878: /* recover our data structure: */ 879: posix_disk = (struct tme_posix_disk *) conn->tme_connection_element->tme_element_private; 880: 881: /* both sides must be disk connections: */ 882: assert(conn->tme_connection_type == TME_CONNECTION_DISK); 883: assert(conn->tme_connection_other->tme_connection_type == TME_CONNECTION_DISK); 884: 885: /* we're always set up to answer calls across the connection, 886: so we only have to do work when the connection has gone full, 887: namely taking the other side of the connection: */ 888: if (state == TME_CONNECTION_FULL) { 889: 890: /* lock the mutex: */ 891: tme_mutex_lock(&posix_disk->tme_posix_disk_mutex); 892: 893: /* save this connection to our list of connections: */ 894: conn_disk = (struct tme_disk_connection *) conn->tme_connection_other; 895: posix_disk->tme_posix_disk_connection = conn_disk; 896: 897: /* unlock the mutex: */ 898: tme_mutex_unlock(&posix_disk->tme_posix_disk_mutex); 899: } 900: 901: return (TME_OK); 902: } 903: 904: /* this makes a new connection side for a posix disk: */ 905: static int 906: _tme_posix_disk_connections_new(struct tme_element *element, 907: const char * const *args, 908: struct tme_connection **_conns, 909: char **_output) 910: { 911: struct tme_posix_disk *posix_disk; 912: struct tme_disk_connection *conn_disk; 913: struct tme_connection *conn; 914: 915: /* recover our data structure: */ 916: posix_disk = (struct tme_posix_disk *) element->tme_element_private; 917: 918: /* if we already have a connection, there's nothing to do: */ 919: if (posix_disk->tme_posix_disk_connection != NULL) { 920: return (TME_OK); 921: } 922: 923: /* create our side of a disk connection: */ 924: conn_disk = tme_new0(struct tme_disk_connection, 1); 925: conn = &conn_disk->tme_disk_connection; 926: 927: /* fill in the generic connection: */ 928: conn->tme_connection_next = *_conns; 929: conn->tme_connection_type = TME_CONNECTION_DISK; 930: conn->tme_connection_score = tme_disk_connection_score; 931: conn->tme_connection_make = _tme_posix_disk_connection_make; 932: conn->tme_connection_break = _tme_posix_disk_connection_break; 933: 934: /* fill in the disk connection: */ 935: (void) tme_value64_set(&conn_disk->tme_disk_connection_size, 936: posix_disk->tme_posix_disk_stat.st_size); 937: conn_disk->tme_disk_connection_read 938: = _tme_posix_disk_read; 939: if (!(posix_disk->tme_posix_disk_flags 940: & TME_POSIX_DISK_FLAG_RO)) { 941: conn_disk->tme_disk_connection_write 942: = _tme_posix_disk_write; 943: } 944: conn_disk->tme_disk_connection_release 945: = NULL; 946: conn_disk->tme_disk_connection_control 947: = _tme_posix_disk_control; 948: 949: /* return the connection side possibility: */ 950: *_conns = conn; 951: return (TME_OK); 952: } 953: 954: /* the new posix disk function: */ 955: TME_ELEMENT_SUB_NEW_DECL(tme_host_posix,disk) { 956: const char *filename; 957: int flags; 958: unsigned long agg_pre; 959: unsigned long agg_post; 960: int buffers; 961: struct tme_posix_disk *posix_disk; 1.1.1.3 ! root 962: int rc; 1.1 root 963: struct tme_posix_disk_buffer *buffer, **_prev; 964: int arg_i; 965: int usage; 966: 967: /* check our arguments: */ 968: filename = NULL; 969: flags = 0; 970: arg_i = 1; 971: buffers = TME_POSIX_DISK_BUFFER_DEFAULT_COUNT; 972: agg_pre = TME_POSIX_DISK_BUFFER_DEFAULT_AGG_PRE; 973: agg_post = TME_POSIX_DISK_BUFFER_DEFAULT_AGG_POST; 974: usage = FALSE; 975: 976: /* loop reading our arguments: */ 977: for (;;) { 978: 979: /* the filename: */ 980: if (TME_ARG_IS(args[arg_i], "file") 981: && args[arg_i + 1] != NULL 982: && filename == NULL) { 983: filename = args[arg_i + 1]; 984: arg_i += 2; 985: } 986: 987: /* the read-only flag: */ 988: else if (TME_ARG_IS(args[arg_i], "read-only")) { 989: flags |= TME_POSIX_DISK_FLAG_RO; 990: arg_i++; 991: } 992: 993: /* the buffers count: */ 994: else if (TME_ARG_IS(args[arg_i + 0], "buffers") 995: && args[arg_i + 1] != NULL 996: && (buffers = atoi(args[arg_i + 1])) > 0) { 997: arg_i += 2; 998: } 999: 1000: /* the read-behind value, also called the aggregate-pre: */ 1001: else if (TME_ARG_IS(args[arg_i + 0], "read-behind")) { 1002: agg_pre = tme_bus_addr_parse_any(args[arg_i + 1], &usage); 1003: if (usage) { 1004: break; 1005: } 1006: arg_i += 2; 1007: } 1008: 1009: /* the read-ahead value, also called the aggregate-post: */ 1010: else if (TME_ARG_IS(args[arg_i + 0], "read-ahead")) { 1011: agg_post = tme_bus_addr_parse_any(args[arg_i + 1], &usage); 1012: if (usage) { 1013: break; 1014: } 1015: arg_i += 2; 1016: } 1017: 1018: /* if we've run out of arguments: */ 1019: else if (args[arg_i + 0] == NULL) { 1020: 1021: /* we must have been given a filename: */ 1022: if (filename == NULL) { 1023: usage = TRUE; 1024: } 1025: break; 1026: } 1027: 1028: /* this is a bad argument: */ 1029: else { 1030: tme_output_append_error(_output, 1031: "%s %s", 1032: args[arg_i], 1033: _("unexpected")); 1034: usage = TRUE; 1035: break; 1036: } 1037: } 1038: 1039: if (usage) { 1040: tme_output_append_error(_output, 1041: "%s %s file %s [read-only] [buffers %s] [read-behind %s] [read-ahead %s]", 1042: _("usage:"), 1043: args[0], 1044: _("FILENAME"), 1045: _("BUFFER-COUNT"), 1046: _("BYTE-COUNT"), 1047: _("BYTE-COUNT")); 1048: return (EINVAL); 1049: } 1050: 1051: /* start the disk structure: */ 1052: posix_disk = tme_new0(struct tme_posix_disk, 1); 1053: posix_disk->tme_posix_disk_element = element; 1054: tme_mutex_init(&posix_disk->tme_posix_disk_mutex); 1055: posix_disk->tme_posix_disk_buffer_agg_pre = agg_pre; 1056: posix_disk->tme_posix_disk_buffer_agg_post = agg_post; 1057: 1.1.1.3 ! root 1058: /* open the disk: */ ! 1059: rc = _tme_posix_disk_open(posix_disk, ! 1060: filename, ! 1061: flags, ! 1062: _output); ! 1063: if (rc != TME_OK) { ! 1064: tme_free(posix_disk); ! 1065: return (rc); ! 1066: } ! 1067: 1.1 root 1068: /* allocate the buffers: */ 1069: for (_prev = &posix_disk->tme_posix_disk_buffers; 1070: buffers-- > 0; 1071: _prev = &buffer->tme_posix_disk_buffer_next) { 1072: buffer = tme_new0(struct tme_posix_disk_buffer, 1); 1073: buffer->tme_posix_disk_buffer_prev = _prev; 1074: *buffer->tme_posix_disk_buffer_prev = buffer; 1075: } 1076: *_prev = NULL; 1077: 1078: /* fill the element: */ 1079: element->tme_element_private = posix_disk; 1080: element->tme_element_connections_new = _tme_posix_disk_connections_new; 1.1.1.3 ! root 1081: element->tme_element_command = _tme_posix_disk_command; 1.1 root 1082: 1083: return (TME_OK); 1084: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.