Skip to content

Commit 84a967d

Browse files
committed
Enable virtio-blk to access hostOS /dev/ block devices
The user may not always have a disk image but might have a /dev/x block device, such as a USB drive that they want to share with the guest OS. So, allowing this type of virtio-blk source is intuitive. To support this, ioctl is used to retrieve the actual size of the /dev/x block device. This implementation supports both Apple and Linux platforms.
1 parent 80cac81 commit 84a967d

File tree

1 file changed

+54
-9
lines changed

1 file changed

+54
-9
lines changed

src/devices/virtio-blk.c

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,22 @@
55

66
#include <assert.h>
77
#include <fcntl.h>
8+
#include <libgen.h>
89
#include <stdbool.h>
910
#include <stdio.h>
1011
#include <stdlib.h>
1112
#include <string.h>
13+
#include <sys/ioctl.h>
1214
#include <sys/mman.h>
1315
#include <sys/stat.h>
1416
#include <unistd.h>
1517

18+
#if defined(__APPLE__)
19+
#include <sys/disk.h> /* DKIOCGETBLOCKCOUNT and DKIOCGETBLOCKSIZE */
20+
#else
21+
#include <linux/fs.h> /* BLKGETSIZE64 */
22+
#endif
23+
1624
#include "virtio.h"
1725

1826
#define DISK_BLK_SIZE 512
@@ -394,25 +402,57 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
394402
int disk_fd = open(disk_file, O_RDWR);
395403
if (disk_fd < 0) {
396404
rv_log_error("Could not open %s", disk_file);
397-
exit(EXIT_FAILURE);
405+
goto fail;
398406
}
399407

400-
/* Get the disk image size */
401-
struct stat st;
402-
fstat(disk_fd, &st);
403-
VBLK_PRIV(vblk)->disk_size = st.st_size;
408+
const char *disk_file_dirname = dirname(disk_file);
409+
if (!disk_file_dirname) {
410+
rv_log_error("Fail dirname disk_file: %s", disk_file);
411+
goto disk_size_fail;
412+
}
413+
/* Get the disk size */
414+
uint64_t disk_size;
415+
if (strcmp(disk_file_dirname, "/dev") ==
416+
0) { /* from /dev/, leverage ioctl */
417+
#if defined(__APPLE__)
418+
uint32_t block_size;
419+
uint64_t block_count;
420+
if (ioctl(disk_fd, DKIOCGETBLOCKCOUNT, &block_count) == -1) {
421+
rv_log_error("DKIOCGETBLOCKCOUNT failed");
422+
goto disk_size_fail;
423+
}
424+
if (ioctl(disk_fd, DKIOCGETBLOCKSIZE, &block_size) == -1) {
425+
rv_log_error("DKIOCGETBLOCKSIZE failed");
426+
goto disk_size_fail;
427+
}
428+
disk_size = block_count * block_size;
429+
#else /* Linux */
430+
if (ioctl(disk_fd, BLKGETSIZE64, &disk_size) == -1) {
431+
rv_log_error("BLKGETSIZE64 failed");
432+
goto disk_size_fail;
433+
}
434+
#endif
435+
} else { /* other path, stat it as normal file */
436+
struct stat st;
437+
if (fstat(disk_fd, &st) == -1) {
438+
rv_log_error("fstat failed");
439+
goto disk_size_fail;
440+
}
441+
disk_size = st.st_size;
442+
}
443+
VBLK_PRIV(vblk)->disk_size = disk_size;
404444

405445
/* Set up the disk memory */
406446
uint32_t *disk_mem;
407447
#if HAVE_MMAP
408448
disk_mem = mmap(NULL, VBLK_PRIV(vblk)->disk_size, PROT_READ | PROT_WRITE,
409449
MAP_SHARED, disk_fd, 0);
410450
if (disk_mem == MAP_FAILED)
411-
goto err;
451+
goto disk_mem_err;
412452
#else
413453
disk_mem = malloc(VBLK_PRIV(vblk)->disk_size);
414454
if (!disk_mem)
415-
goto err;
455+
goto disk_mem_err;
416456
#endif
417457
assert(!(((uintptr_t) disk_mem) & 0b11));
418458
close(disk_fd);
@@ -423,9 +463,14 @@ uint32_t *virtio_blk_init(virtio_blk_state_t *vblk, char *disk_file)
423463

424464
return disk_mem;
425465

426-
err:
466+
disk_mem_err:
427467
rv_log_error("Could not map disk %s", disk_file);
428-
return NULL;
468+
469+
disk_size_fail:
470+
close(disk_fd);
471+
472+
fail:
473+
exit(EXIT_FAILURE);
429474
}
430475

431476
virtio_blk_state_t *vblk_new()

0 commit comments

Comments
 (0)