| 1 | /* block-ram.c |
|---|
| 2 | * |
|---|
| 3 | * Fast Ramdisk implementation. |
|---|
| 4 | * |
|---|
| 5 | * (c) 2006 Andrew Warfield and Julian Chesterfield |
|---|
| 6 | * |
|---|
| 7 | * This program is free software; you can redistribute it and/or |
|---|
| 8 | * modify it under the terms of the GNU General Public License version 2 |
|---|
| 9 | * as published by the Free Software Foundation; or, when distributed |
|---|
| 10 | * separately from the Linux kernel or incorporated into other |
|---|
| 11 | * software packages, subject to the following license: |
|---|
| 12 | * |
|---|
| 13 | * Permission is hereby granted, free of charge, to any person obtaining a copy |
|---|
| 14 | * of this source file (the "Software"), to deal in the Software without |
|---|
| 15 | * restriction, including without limitation the rights to use, copy, modify, |
|---|
| 16 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, |
|---|
| 17 | * and to permit persons to whom the Software is furnished to do so, subject to |
|---|
| 18 | * the following conditions: |
|---|
| 19 | * |
|---|
| 20 | * The above copyright notice and this permission notice shall be included in |
|---|
| 21 | * all copies or substantial portions of the Software. |
|---|
| 22 | * |
|---|
| 23 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|---|
| 24 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|---|
| 25 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|---|
| 26 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|---|
| 27 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
|---|
| 28 | * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS |
|---|
| 29 | * IN THE SOFTWARE. |
|---|
| 30 | */ |
|---|
| 31 | |
|---|
| 32 | #include <errno.h> |
|---|
| 33 | #include <fcntl.h> |
|---|
| 34 | #include <stdio.h> |
|---|
| 35 | #include <stdlib.h> |
|---|
| 36 | #include <unistd.h> |
|---|
| 37 | #include <sys/statvfs.h> |
|---|
| 38 | #include <sys/stat.h> |
|---|
| 39 | #include <sys/ioctl.h> |
|---|
| 40 | #include <linux/fs.h> |
|---|
| 41 | #include <string.h> |
|---|
| 42 | #include "tapdisk.h" |
|---|
| 43 | |
|---|
| 44 | #define MAX_DISK_SIZE 1024000 /*500MB disk limit*/ |
|---|
| 45 | |
|---|
| 46 | char *img; |
|---|
| 47 | long int disksector_size; |
|---|
| 48 | long int disksize; |
|---|
| 49 | long int diskinfo; |
|---|
| 50 | static int connections = 0; |
|---|
| 51 | |
|---|
| 52 | struct tdram_state { |
|---|
| 53 | int fd; |
|---|
| 54 | int poll_pipe[2]; /* dummy fd for polling on */ |
|---|
| 55 | }; |
|---|
| 56 | |
|---|
| 57 | /*Get Image size, secsize*/ |
|---|
| 58 | static int get_image_info(struct td_state *s, int fd) |
|---|
| 59 | { |
|---|
| 60 | int ret; |
|---|
| 61 | long size; |
|---|
| 62 | unsigned long total_size; |
|---|
| 63 | struct statvfs statBuf; |
|---|
| 64 | struct stat stat; |
|---|
| 65 | |
|---|
| 66 | ret = fstat(fd, &stat); |
|---|
| 67 | if (ret != 0) { |
|---|
| 68 | DPRINTF("ERROR: fstat failed, Couldn't stat image"); |
|---|
| 69 | return -EINVAL; |
|---|
| 70 | } |
|---|
| 71 | |
|---|
| 72 | if (S_ISBLK(stat.st_mode)) { |
|---|
| 73 | /*Accessing block device directly*/ |
|---|
| 74 | s->size = 0; |
|---|
| 75 | if (ioctl(fd,BLKGETSIZE,&s->size)!=0) { |
|---|
| 76 | DPRINTF("ERR: BLKGETSIZE failed, couldn't stat image"); |
|---|
| 77 | return -EINVAL; |
|---|
| 78 | } |
|---|
| 79 | |
|---|
| 80 | DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " |
|---|
| 81 | "sector_shift [%llu]\n", |
|---|
| 82 | (long long unsigned)(s->size << SECTOR_SHIFT), |
|---|
| 83 | (long long unsigned)s->size); |
|---|
| 84 | |
|---|
| 85 | /*Get the sector size*/ |
|---|
| 86 | #if defined(BLKSSZGET) |
|---|
| 87 | { |
|---|
| 88 | int arg; |
|---|
| 89 | s->sector_size = DEFAULT_SECTOR_SIZE; |
|---|
| 90 | ioctl(fd, BLKSSZGET, &s->sector_size); |
|---|
| 91 | |
|---|
| 92 | if (s->sector_size != DEFAULT_SECTOR_SIZE) |
|---|
| 93 | DPRINTF("Note: sector size is %ld (not %d)\n", |
|---|
| 94 | s->sector_size, DEFAULT_SECTOR_SIZE); |
|---|
| 95 | } |
|---|
| 96 | #else |
|---|
| 97 | s->sector_size = DEFAULT_SECTOR_SIZE; |
|---|
| 98 | #endif |
|---|
| 99 | |
|---|
| 100 | } else { |
|---|
| 101 | /*Local file? try fstat instead*/ |
|---|
| 102 | s->size = (stat.st_size >> SECTOR_SHIFT); |
|---|
| 103 | s->sector_size = DEFAULT_SECTOR_SIZE; |
|---|
| 104 | DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " |
|---|
| 105 | "sector_shift [%llu]\n", |
|---|
| 106 | (long long unsigned)(s->size << SECTOR_SHIFT), |
|---|
| 107 | (long long unsigned)s->size); |
|---|
| 108 | } |
|---|
| 109 | |
|---|
| 110 | if (s->size == 0) { |
|---|
| 111 | s->size =((uint64_t) MAX_DISK_SIZE); |
|---|
| 112 | s->sector_size = DEFAULT_SECTOR_SIZE; |
|---|
| 113 | } |
|---|
| 114 | s->info = 0; |
|---|
| 115 | |
|---|
| 116 | /*Store variables locally*/ |
|---|
| 117 | disksector_size = s->sector_size; |
|---|
| 118 | disksize = s->size; |
|---|
| 119 | diskinfo = s->info; |
|---|
| 120 | DPRINTF("Image sector_size: \n\t[%lu]\n", |
|---|
| 121 | s->sector_size); |
|---|
| 122 | |
|---|
| 123 | return 0; |
|---|
| 124 | } |
|---|
| 125 | |
|---|
| 126 | static inline void init_fds(struct disk_driver *dd) |
|---|
| 127 | { |
|---|
| 128 | int i; |
|---|
| 129 | struct tdram_state *prv = (struct tdram_state *)dd->private; |
|---|
| 130 | |
|---|
| 131 | for(i =0 ; i < MAX_IOFD; i++) |
|---|
| 132 | dd->io_fd[i] = 0; |
|---|
| 133 | |
|---|
| 134 | dd->io_fd[0] = prv->poll_pipe[0]; |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | /* Open the disk file and initialize ram state. */ |
|---|
| 138 | int tdram_open (struct disk_driver *dd, const char *name, td_flag_t flags) |
|---|
| 139 | { |
|---|
| 140 | char *p; |
|---|
| 141 | uint64_t size; |
|---|
| 142 | int i, fd, ret = 0, count = 0, o_flags; |
|---|
| 143 | struct td_state *s = dd->td_state; |
|---|
| 144 | struct tdram_state *prv = (struct tdram_state *)dd->private; |
|---|
| 145 | |
|---|
| 146 | connections++; |
|---|
| 147 | |
|---|
| 148 | /* set up a pipe so that we can hand back a poll fd that won't fire.*/ |
|---|
| 149 | ret = pipe(prv->poll_pipe); |
|---|
| 150 | if (ret != 0) |
|---|
| 151 | return (0 - errno); |
|---|
| 152 | |
|---|
| 153 | if (connections > 1) { |
|---|
| 154 | s->sector_size = disksector_size; |
|---|
| 155 | s->size = disksize; |
|---|
| 156 | s->info = diskinfo; |
|---|
| 157 | DPRINTF("Image already open, returning parameters:\n"); |
|---|
| 158 | DPRINTF("Image size: \n\tpre sector_shift [%llu]\n\tpost " |
|---|
| 159 | "sector_shift [%llu]\n", |
|---|
| 160 | (long long unsigned)(s->size << SECTOR_SHIFT), |
|---|
| 161 | (long long unsigned)s->size); |
|---|
| 162 | DPRINTF("Image sector_size: \n\t[%lu]\n", |
|---|
| 163 | s->sector_size); |
|---|
| 164 | |
|---|
| 165 | prv->fd = -1; |
|---|
| 166 | goto done; |
|---|
| 167 | } |
|---|
| 168 | |
|---|
| 169 | /* Open the file */ |
|---|
| 170 | o_flags = O_DIRECT | O_LARGEFILE | |
|---|
| 171 | ((flags == TD_RDONLY) ? O_RDONLY : O_RDWR); |
|---|
| 172 | fd = open(name, o_flags); |
|---|
| 173 | |
|---|
| 174 | if ((fd == -1) && (errno == EINVAL)) { |
|---|
| 175 | |
|---|
| 176 | /* Maybe O_DIRECT isn't supported. */ |
|---|
| 177 | o_flags &= ~O_DIRECT; |
|---|
| 178 | fd = open(name, o_flags); |
|---|
| 179 | if (fd != -1) DPRINTF("WARNING: Accessing image without" |
|---|
| 180 | "O_DIRECT! (%s)\n", name); |
|---|
| 181 | |
|---|
| 182 | } else if (fd != -1) DPRINTF("open(%s) with O_DIRECT\n", name); |
|---|
| 183 | |
|---|
| 184 | if (fd == -1) { |
|---|
| 185 | DPRINTF("Unable to open [%s]!\n",name); |
|---|
| 186 | ret = 0 - errno; |
|---|
| 187 | goto done; |
|---|
| 188 | } |
|---|
| 189 | |
|---|
| 190 | prv->fd = fd; |
|---|
| 191 | |
|---|
| 192 | ret = get_image_info(s, fd); |
|---|
| 193 | size = MAX_DISK_SIZE; |
|---|
| 194 | |
|---|
| 195 | if (s->size > size) { |
|---|
| 196 | DPRINTF("Disk exceeds limit, must be less than [%d]MB", |
|---|
| 197 | (MAX_DISK_SIZE<<SECTOR_SHIFT)>>20); |
|---|
| 198 | return -ENOMEM; |
|---|
| 199 | } |
|---|
| 200 | |
|---|
| 201 | /*Read the image into memory*/ |
|---|
| 202 | p = img = malloc(s->size << SECTOR_SHIFT); |
|---|
| 203 | if (img == NULL) { |
|---|
| 204 | DPRINTF("Mem malloc failed\n"); |
|---|
| 205 | return -1; |
|---|
| 206 | } |
|---|
| 207 | DPRINTF("Reading %llu bytes.......",(long long unsigned)s->size << SECTOR_SHIFT); |
|---|
| 208 | |
|---|
| 209 | for (i = 0; i < s->size; i++) { |
|---|
| 210 | ret = read(prv->fd, p, s->sector_size); |
|---|
| 211 | if (ret != s->sector_size) { |
|---|
| 212 | ret = 0 - errno; |
|---|
| 213 | break; |
|---|
| 214 | } else { |
|---|
| 215 | count += ret; |
|---|
| 216 | p = img + count; |
|---|
| 217 | } |
|---|
| 218 | } |
|---|
| 219 | DPRINTF("[%d]\n",count); |
|---|
| 220 | if (count != s->size << SECTOR_SHIFT) { |
|---|
| 221 | ret = -1; |
|---|
| 222 | } else { |
|---|
| 223 | ret = 0; |
|---|
| 224 | } |
|---|
| 225 | |
|---|
| 226 | init_fds(dd); |
|---|
| 227 | done: |
|---|
| 228 | return ret; |
|---|
| 229 | } |
|---|
| 230 | |
|---|
| 231 | int tdram_queue_read(struct disk_driver *dd, uint64_t sector, |
|---|
| 232 | int nb_sectors, char *buf, td_callback_t cb, |
|---|
| 233 | int id, void *private) |
|---|
| 234 | { |
|---|
| 235 | struct td_state *s = dd->td_state; |
|---|
| 236 | struct tdram_state *prv = (struct tdram_state *)dd->private; |
|---|
| 237 | int size = nb_sectors * s->sector_size; |
|---|
| 238 | uint64_t offset = sector * (uint64_t)s->sector_size; |
|---|
| 239 | |
|---|
| 240 | memcpy(buf, img + offset, size); |
|---|
| 241 | |
|---|
| 242 | return cb(dd, 0, sector, nb_sectors, id, private); |
|---|
| 243 | } |
|---|
| 244 | |
|---|
| 245 | int tdram_queue_write(struct disk_driver *dd, uint64_t sector, |
|---|
| 246 | int nb_sectors, char *buf, td_callback_t cb, |
|---|
| 247 | int id, void *private) |
|---|
| 248 | { |
|---|
| 249 | struct td_state *s = dd->td_state; |
|---|
| 250 | struct tdram_state *prv = (struct tdram_state *)dd->private; |
|---|
| 251 | int size = nb_sectors * s->sector_size; |
|---|
| 252 | uint64_t offset = sector * (uint64_t)s->sector_size; |
|---|
| 253 | |
|---|
| 254 | /* We assume that write access is controlled |
|---|
| 255 | * at a higher level for multiple disks */ |
|---|
| 256 | memcpy(img + offset, buf, size); |
|---|
| 257 | |
|---|
| 258 | return cb(dd, 0, sector, nb_sectors, id, private); |
|---|
| 259 | } |
|---|
| 260 | |
|---|
| 261 | int tdram_submit(struct disk_driver *dd) |
|---|
| 262 | { |
|---|
| 263 | return 0; |
|---|
| 264 | } |
|---|
| 265 | |
|---|
| 266 | int tdram_close(struct disk_driver *dd) |
|---|
| 267 | { |
|---|
| 268 | struct tdram_state *prv = (struct tdram_state *)dd->private; |
|---|
| 269 | |
|---|
| 270 | connections--; |
|---|
| 271 | |
|---|
| 272 | return 0; |
|---|
| 273 | } |
|---|
| 274 | |
|---|
| 275 | int tdram_do_callbacks(struct disk_driver *dd, int sid) |
|---|
| 276 | { |
|---|
| 277 | /* always ask for a kick */ |
|---|
| 278 | return 1; |
|---|
| 279 | } |
|---|
| 280 | |
|---|
| 281 | int tdram_get_parent_id(struct disk_driver *dd, struct disk_id *id) |
|---|
| 282 | { |
|---|
| 283 | return TD_NO_PARENT; |
|---|
| 284 | } |
|---|
| 285 | |
|---|
| 286 | int tdram_validate_parent(struct disk_driver *dd, |
|---|
| 287 | struct disk_driver *parent, td_flag_t flags) |
|---|
| 288 | { |
|---|
| 289 | return -EINVAL; |
|---|
| 290 | } |
|---|
| 291 | |
|---|
| 292 | struct tap_disk tapdisk_ram = { |
|---|
| 293 | .disk_type = "tapdisk_ram", |
|---|
| 294 | .private_data_size = sizeof(struct tdram_state), |
|---|
| 295 | .td_open = tdram_open, |
|---|
| 296 | .td_queue_read = tdram_queue_read, |
|---|
| 297 | .td_queue_write = tdram_queue_write, |
|---|
| 298 | .td_submit = tdram_submit, |
|---|
| 299 | .td_close = tdram_close, |
|---|
| 300 | .td_do_callbacks = tdram_do_callbacks, |
|---|
| 301 | .td_get_parent_id = tdram_get_parent_id, |
|---|
| 302 | .td_validate_parent = tdram_validate_parent |
|---|
| 303 | }; |
|---|