19f532d00SPekka Enberg #include "kvm/disk-image.h" 29f532d00SPekka Enberg 3c4d7847bSPekka Enberg #include "kvm/read-write.h" 486835cedSPrasad Joshi #include "kvm/qcow.h" 59f532d00SPekka Enberg #include "kvm/util.h" 69f532d00SPekka Enberg 7*6ec60cfdSPekka Enberg #include <linux/fs.h> /* for BLKGETSIZE64 */ 8*6ec60cfdSPekka Enberg 9*6ec60cfdSPekka Enberg #include <sys/ioctl.h> 109f532d00SPekka Enberg #include <sys/types.h> 1133d33901SPekka Enberg #include <inttypes.h> 129f532d00SPekka Enberg #include <sys/mman.h> 139f532d00SPekka Enberg #include <sys/stat.h> 145646450dSPekka Enberg #include <stdbool.h> 159f532d00SPekka Enberg #include <stddef.h> 169f532d00SPekka Enberg #include <stdlib.h> 179f532d00SPekka Enberg #include <unistd.h> 189f532d00SPekka Enberg #include <fcntl.h> 199f532d00SPekka Enberg 20f4ff38dfSPrasad Joshi struct disk_image *disk_image__new(int fd, uint64_t size, struct disk_image_operations *ops) 219f532d00SPekka Enberg { 22fffb37a9SPekka Enberg struct disk_image *self; 239f532d00SPekka Enberg 24fffb37a9SPekka Enberg self = malloc(sizeof *self); 25fffb37a9SPekka Enberg if (!self) 26fffb37a9SPekka Enberg return NULL; 279f532d00SPekka Enberg 28fffb37a9SPekka Enberg self->fd = fd; 29fffb37a9SPekka Enberg self->size = size; 30fffb37a9SPekka Enberg self->ops = ops; 31f4ff38dfSPrasad Joshi return self; 32f4ff38dfSPrasad Joshi } 33f4ff38dfSPrasad Joshi 34f4ff38dfSPrasad Joshi struct disk_image *disk_image__new_readonly(int fd, uint64_t size, struct disk_image_operations *ops) 35f4ff38dfSPrasad Joshi { 36f4ff38dfSPrasad Joshi struct disk_image *self; 37f4ff38dfSPrasad Joshi 38f4ff38dfSPrasad Joshi self = disk_image__new(fd, size, ops); 39f4ff38dfSPrasad Joshi if (!self) 40f4ff38dfSPrasad Joshi return NULL; 41f4ff38dfSPrasad Joshi 429ac38fe1SSasha Levin self->priv = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_NORESERVE, fd, 0); 439ac38fe1SSasha Levin if (self->priv == MAP_FAILED) 449ac38fe1SSasha Levin die("mmap() failed"); 45fffb37a9SPekka Enberg return self; 469f532d00SPekka Enberg } 479f532d00SPekka Enberg 48f4ff38dfSPrasad Joshi 49499f3bedSPekka Enberg static int raw_image__read_sector(struct disk_image *self, uint64_t sector, void *dst, uint32_t dst_len) 509f532d00SPekka Enberg { 519f532d00SPekka Enberg uint64_t offset = sector << SECTOR_SHIFT; 529f532d00SPekka Enberg 535a24a9f2SPekka Enberg if (offset + dst_len > self->size) 549f532d00SPekka Enberg return -1; 559f532d00SPekka Enberg 566b7deb02SPekka Enberg if (pread_in_full(self->fd, dst, dst_len, offset) < 0) 57c4d7847bSPekka Enberg return -1; 589f532d00SPekka Enberg 599f532d00SPekka Enberg return 0; 609f532d00SPekka Enberg } 61258dd093SPekka Enberg 62499f3bedSPekka Enberg static int raw_image__write_sector(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len) 63258dd093SPekka Enberg { 64258dd093SPekka Enberg uint64_t offset = sector << SECTOR_SHIFT; 65258dd093SPekka Enberg 66258dd093SPekka Enberg if (offset + src_len > self->size) 67258dd093SPekka Enberg return -1; 68258dd093SPekka Enberg 696b7deb02SPekka Enberg if (pwrite_in_full(self->fd, src, src_len, offset) < 0) 70c4d7847bSPekka Enberg return -1; 71258dd093SPekka Enberg 72258dd093SPekka Enberg return 0; 73258dd093SPekka Enberg } 74499f3bedSPekka Enberg 759ac38fe1SSasha Levin static int raw_image__read_sector_ro_mmap(struct disk_image *self, uint64_t sector, void *dst, uint32_t dst_len) 769ac38fe1SSasha Levin { 779ac38fe1SSasha Levin uint64_t offset = sector << SECTOR_SHIFT; 789ac38fe1SSasha Levin 799ac38fe1SSasha Levin if (offset + dst_len > self->size) 809ac38fe1SSasha Levin return -1; 819ac38fe1SSasha Levin 829ac38fe1SSasha Levin memcpy(dst, self->priv + offset, dst_len); 839ac38fe1SSasha Levin 849ac38fe1SSasha Levin return 0; 859ac38fe1SSasha Levin } 869ac38fe1SSasha Levin 879ac38fe1SSasha Levin static int raw_image__write_sector_ro_mmap(struct disk_image *self, uint64_t sector, void *src, uint32_t src_len) 889ac38fe1SSasha Levin { 899ac38fe1SSasha Levin uint64_t offset = sector << SECTOR_SHIFT; 909ac38fe1SSasha Levin 919ac38fe1SSasha Levin if (offset + src_len > self->size) 929ac38fe1SSasha Levin return -1; 939ac38fe1SSasha Levin 949ac38fe1SSasha Levin memcpy(self->priv + offset, src, src_len); 959ac38fe1SSasha Levin 969ac38fe1SSasha Levin return 0; 979ac38fe1SSasha Levin } 989ac38fe1SSasha Levin 99177a5815SPrasad Joshi static void raw_image__close_sector_ro_mmap(struct disk_image *self) 100177a5815SPrasad Joshi { 101177a5815SPrasad Joshi if (self->priv != MAP_FAILED) 102177a5815SPrasad Joshi munmap(self->priv, self->size); 103177a5815SPrasad Joshi } 104177a5815SPrasad Joshi 105499f3bedSPekka Enberg static struct disk_image_operations raw_image_ops = { 106499f3bedSPekka Enberg .read_sector = raw_image__read_sector, 107499f3bedSPekka Enberg .write_sector = raw_image__write_sector, 108499f3bedSPekka Enberg }; 109499f3bedSPekka Enberg 1109ac38fe1SSasha Levin static struct disk_image_operations raw_image_ro_mmap_ops = { 1119ac38fe1SSasha Levin .read_sector = raw_image__read_sector_ro_mmap, 1129ac38fe1SSasha Levin .write_sector = raw_image__write_sector_ro_mmap, 113177a5815SPrasad Joshi .close = raw_image__close_sector_ro_mmap, 1149ac38fe1SSasha Levin }; 1159ac38fe1SSasha Levin 116*6ec60cfdSPekka Enberg static struct disk_image *raw_image__probe(int fd, struct stat *st, bool readonly) 117499f3bedSPekka Enberg { 118*6ec60cfdSPekka Enberg if (readonly) 119*6ec60cfdSPekka Enberg return disk_image__new_readonly(fd, st->st_size, &raw_image_ro_mmap_ops); 120*6ec60cfdSPekka Enberg else 121*6ec60cfdSPekka Enberg return disk_image__new(fd, st->st_size, &raw_image_ops); 122*6ec60cfdSPekka Enberg } 123499f3bedSPekka Enberg 124*6ec60cfdSPekka Enberg static struct disk_image *blkdev__probe(const char *filename, struct stat *st) 125*6ec60cfdSPekka Enberg { 126*6ec60cfdSPekka Enberg uint64_t size; 127*6ec60cfdSPekka Enberg int fd; 128*6ec60cfdSPekka Enberg 129*6ec60cfdSPekka Enberg if (!S_ISBLK(st->st_mode)) 130499f3bedSPekka Enberg return NULL; 131499f3bedSPekka Enberg 132*6ec60cfdSPekka Enberg fd = open(filename, O_RDONLY); 133*6ec60cfdSPekka Enberg if (fd < 0) 134*6ec60cfdSPekka Enberg return NULL; 135*6ec60cfdSPekka Enberg 136*6ec60cfdSPekka Enberg if (ioctl(fd, BLKGETSIZE64, &size) < 0) 137*6ec60cfdSPekka Enberg return NULL; 138*6ec60cfdSPekka Enberg 139*6ec60cfdSPekka Enberg return disk_image__new_readonly(fd, size, &raw_image_ro_mmap_ops); 140499f3bedSPekka Enberg } 141499f3bedSPekka Enberg 1429ac38fe1SSasha Levin struct disk_image *disk_image__open(const char *filename, bool readonly) 143499f3bedSPekka Enberg { 144499f3bedSPekka Enberg struct disk_image *self; 145*6ec60cfdSPekka Enberg struct stat st; 146499f3bedSPekka Enberg int fd; 147499f3bedSPekka Enberg 148*6ec60cfdSPekka Enberg if (stat(filename, &st) < 0) 149*6ec60cfdSPekka Enberg return NULL; 150*6ec60cfdSPekka Enberg 151*6ec60cfdSPekka Enberg if (S_ISBLK(st.st_mode)) 152*6ec60cfdSPekka Enberg return blkdev__probe(filename, &st); 153*6ec60cfdSPekka Enberg 1549ac38fe1SSasha Levin fd = open(filename, readonly ? O_RDONLY : O_RDWR); 155499f3bedSPekka Enberg if (fd < 0) 156499f3bedSPekka Enberg return NULL; 157499f3bedSPekka Enberg 15886835cedSPrasad Joshi self = qcow_probe(fd); 15986835cedSPrasad Joshi if (self) 16086835cedSPrasad Joshi return self; 16186835cedSPrasad Joshi 162*6ec60cfdSPekka Enberg self = raw_image__probe(fd, &st, readonly); 163499f3bedSPekka Enberg if (self) 164499f3bedSPekka Enberg return self; 165499f3bedSPekka Enberg 166d6c58e5bSPrasad Joshi if (close(fd) < 0) 167499f3bedSPekka Enberg warning("close() failed"); 168499f3bedSPekka Enberg 169499f3bedSPekka Enberg return NULL; 170499f3bedSPekka Enberg } 171499f3bedSPekka Enberg 172499f3bedSPekka Enberg void disk_image__close(struct disk_image *self) 173499f3bedSPekka Enberg { 17482d94c50SIngo Molnar /* If there was no disk image then there's nothing to do: */ 17582d94c50SIngo Molnar if (!self) 17682d94c50SIngo Molnar return; 17782d94c50SIngo Molnar 178499f3bedSPekka Enberg if (self->ops->close) 179499f3bedSPekka Enberg self->ops->close(self); 180499f3bedSPekka Enberg 181499f3bedSPekka Enberg if (close(self->fd) < 0) 182499f3bedSPekka Enberg warning("close() failed"); 183499f3bedSPekka Enberg 184499f3bedSPekka Enberg free(self); 185499f3bedSPekka Enberg } 186