From 52b7ae3152cde27c024fea5ffbd7499b305bdfd7 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sun, 13 Feb 2011 07:36:26 +1100 Subject: [PATCH] LD_PRELOAD: add dup2 support so we can use it with dd --- README | 2 + src/ld_iscsi.c | 214 +++++++++++++++++++++++++++++++++---------------- 2 files changed, 148 insertions(+), 68 deletions(-) diff --git a/README b/README index 53d993e..6bc48c6 100644 --- a/README +++ b/README @@ -94,6 +94,8 @@ Change: 1970-01-01 10:00:00.000000000 +1000 The cat command, which allows you to read/dump a iSCSI LUN to a file : $ LD_PRELOAD=./bin/ld_iscsi.so cat iscsi://127.0.0.1:3262/iqn.ronnie.test/2 >copy_of_iscsi_lun +Or using dd even : +LD_PRELOAD=./bin/ld_iscsi.so strace -o x -f dd if=iscsi://127.0.0.1:3262/iqn.ronnie.test/2 of=copy_of_LUN bs=10M count=1 bs=1M count=10 The LD_PRELOAD hack is incomplete and needs more functions to be intercepted before becomming fully functional. Patches welcome! diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c index 5a062a0..00ec834 100644 --- a/src/ld_iscsi.c +++ b/src/ld_iscsi.c @@ -9,17 +9,20 @@ #include #include "iscsi.h" +#include "iscsi-private.h" #include "scsi-lowlevel.h" #include #include -#define ISCSI_FD_MASK 0x7fffff00 -#define ISCSI_FD_NUM 0x000000ff - static char *initiator = "iqn.2011-02.ronnie:ld_iscsi"; +#define ISCSI_MAX_FD 255 + struct iscsi_fd_list { + int is_iscsi; + int dup2fd; + int in_flight; struct iscsi_context *iscsi; int lun; uint32_t block_size; @@ -27,77 +30,62 @@ struct iscsi_fd_list { off_t offset; }; -static struct iscsi_fd_list iscsi_fd_list[ISCSI_FD_NUM]; +static struct iscsi_fd_list iscsi_fd_list[ISCSI_MAX_FD]; int (*real_open)(__const char *path, int flags, mode_t mode); int open(const char *path, int flags, mode_t mode) { + int fd; + if (!strncmp(path, "iscsi:", 6)) { + struct iscsi_context *iscsi; struct iscsi_url *iscsi_url; struct scsi_task *task; struct scsi_readcapacity10 *rc10; - int i; - for (i = 0; i < ISCSI_FD_NUM; i++) { - if (iscsi_fd_list[i].iscsi == NULL) { - iscsi_fd_list[i].iscsi = iscsi_create_context(initiator); - break; - } - } - - if (i == ISCSI_FD_NUM) { + iscsi = iscsi_create_context(initiator); + if (iscsi == NULL) { fprintf(stderr, "ld-iscsi: Failed to create context\n"); - errno = ENFILE; - return -1; - } - - if (iscsi_fd_list[i].iscsi == NULL) { - fprintf(stderr, "ld-iscsi: Failed to create context\n"); - iscsi_fd_list[i].iscsi = NULL; errno = ENOMEM; return -1; } - iscsi_url = iscsi_parse_full_url(iscsi_fd_list[i].iscsi, path); + iscsi_url = iscsi_parse_full_url(iscsi, path); if (iscsi_url == NULL) { fprintf(stderr, "ld-iscsi: Failed to parse URL: %s\n", - iscsi_get_error(iscsi_fd_list[i].iscsi)); - iscsi_destroy_context(iscsi_fd_list[i].iscsi); - iscsi_fd_list[i].iscsi = NULL; + iscsi_get_error(iscsi)); + iscsi_destroy_context(iscsi); errno = EINVAL; return -1; } - iscsi_set_targetname(iscsi_fd_list[i].iscsi, iscsi_url->target); - iscsi_set_session_type(iscsi_fd_list[i].iscsi, ISCSI_SESSION_NORMAL); - iscsi_set_header_digest(iscsi_fd_list[i].iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); + iscsi_set_targetname(iscsi, iscsi_url->target); + iscsi_set_session_type(iscsi, ISCSI_SESSION_NORMAL); + iscsi_set_header_digest(iscsi, ISCSI_HEADER_DIGEST_NONE_CRC32C); if (iscsi_url->user != NULL) { - if (iscsi_set_initiator_username_pwd(iscsi_fd_list[i].iscsi, iscsi_url->user, iscsi_url->passwd) != 0) { + if (iscsi_set_initiator_username_pwd(iscsi, iscsi_url->user, iscsi_url->passwd) != 0) { fprintf(stderr, "Failed to set initiator username and password\n"); - iscsi_destroy_context(iscsi_fd_list[i].iscsi); - iscsi_fd_list[i].iscsi = NULL; + iscsi_destroy_context(iscsi); errno = ENOMEM; return -1; } } - if (iscsi_full_connect_sync(iscsi_fd_list[i].iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { - fprintf(stderr, "ld-iscsi: Login Failed. %s\n", iscsi_get_error(iscsi_fd_list[i].iscsi)); + if (iscsi_full_connect_sync(iscsi, iscsi_url->portal, iscsi_url->lun) != 0) { + fprintf(stderr, "ld-iscsi: Login Failed. %s\n", iscsi_get_error(iscsi)); iscsi_destroy_url(iscsi_url); - iscsi_destroy_context(iscsi_fd_list[i].iscsi); - iscsi_fd_list[i].iscsi = NULL; + iscsi_destroy_context(iscsi); errno = EIO; return -1; } - task = iscsi_readcapacity10_sync(iscsi_fd_list[i].iscsi, iscsi_url->lun, 0, 0); + task = iscsi_readcapacity10_sync(iscsi, iscsi_url->lun, 0, 0); if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "ld-iscsi: failed to send readcapacity command\n"); iscsi_destroy_url(iscsi_url); - iscsi_destroy_context(iscsi_fd_list[i].iscsi); - iscsi_fd_list[i].iscsi = NULL; + iscsi_destroy_context(iscsi); errno = EIO; return -1; } @@ -107,61 +95,109 @@ int open(const char *path, int flags, mode_t mode) fprintf(stderr, "ld-iscsi: failed to unmarshall readcapacity10 data\n"); scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); - iscsi_destroy_context(iscsi_fd_list[i].iscsi); - iscsi_fd_list[i].iscsi = NULL; + iscsi_destroy_context(iscsi); errno = EIO; return -1; } - iscsi_fd_list[i].block_size = rc10->block_size; - iscsi_fd_list[i].num_blocks = rc10->lba; - iscsi_fd_list[i].offset = 0; - iscsi_fd_list[i].lun = iscsi_url->lun; + fd = iscsi_get_fd(iscsi); + if (fd >= ISCSI_MAX_FD) { + fprintf(stderr, "ld-iscsi: Too many files open\n"); + iscsi_destroy_url(iscsi_url); + iscsi_destroy_context(iscsi); + errno = ENFILE; + return -1; + } + + iscsi_fd_list[fd].is_iscsi = 1; + iscsi_fd_list[fd].dup2fd = -1; + iscsi_fd_list[fd].iscsi = iscsi; + iscsi_fd_list[fd].block_size = rc10->block_size; + iscsi_fd_list[fd].num_blocks = rc10->lba; + iscsi_fd_list[fd].offset = 0; + iscsi_fd_list[fd].lun = iscsi_url->lun; scsi_free_scsi_task(task); iscsi_destroy_url(iscsi_url); - return ISCSI_FD_MASK | i; + return fd; } - return(real_open(path, flags, mode)); + return real_open(path, flags, mode); } int (*real_close)(int fd); int close(int fd) { - if ((fd & ISCSI_FD_MASK) == ISCSI_FD_MASK) { + if (iscsi_fd_list[fd].is_iscsi == 1) { int i; - i = fd & ISCSI_FD_NUM; + if (iscsi_fd_list[fd].dup2fd >= 0) { + iscsi_fd_list[fd].is_iscsi = 0; + iscsi_fd_list[fd].dup2fd = -1; + real_close(fd); + return 0; + } - iscsi_destroy_context(iscsi_fd_list[i].iscsi); - iscsi_fd_list[i].iscsi = NULL; + /* are there any FDs dup2ed onto this ? */ + for(i = 0; i < ISCSI_MAX_FD; i++) { + if (iscsi_fd_list[i].dup2fd == fd) { + break; + } + } + if (i < ISCSI_MAX_FD) { + int j; + + /* yes there are DUPs onto fd, make i the new real device and repoint all other + * duplicates + */ + memcpy(&iscsi_fd_list[i], &iscsi_fd_list[fd], sizeof(struct iscsi_fd_list)); + iscsi_fd_list[i].dup2fd = -1; + + memset(&iscsi_fd_list[fd], 0, sizeof(struct iscsi_fd_list)); + iscsi_fd_list[fd].dup2fd = -1; + + iscsi_fd_list[i].iscsi->fd = i; + real_close(fd); + + for(j = 0; j < ISCSI_MAX_FD; j++) { + if (j != i && iscsi_fd_list[j].dup2fd == fd) { + iscsi_fd_list[j].dup2fd = i; + } + } + + return 0; + } + + iscsi_fd_list[fd].is_iscsi = 0; + iscsi_fd_list[fd].dup2fd = -1; + iscsi_destroy_context(iscsi_fd_list[fd].iscsi); + iscsi_fd_list[fd].iscsi = NULL; return 0; } - return(real_close(fd)); + return real_close(fd); } int (*real_fxstat)(int ver, int fd, struct stat *buf); int __fxstat(int ver, int fd, struct stat *buf) { - if ((fd & ISCSI_FD_MASK) == ISCSI_FD_MASK) { - int i; - - i = fd & ISCSI_FD_NUM; + if (iscsi_fd_list[fd].is_iscsi == 1) { + if (iscsi_fd_list[fd].dup2fd >= 0) { + return __fxstat(ver, iscsi_fd_list[fd].dup2fd, buf); + } memset(buf, 0, sizeof(struct stat)); buf->st_mode = S_IRUSR | S_IRGRP | S_IROTH | S_IFREG; - buf->st_size = iscsi_fd_list[i].num_blocks * iscsi_fd_list[i].block_size; + buf->st_size = iscsi_fd_list[fd].num_blocks * iscsi_fd_list[fd].block_size; return 0; } - return(real_fxstat(ver, fd, buf)); + return real_fxstat(ver, fd, buf); } @@ -182,7 +218,7 @@ int __lxstat(int ver, const char *path, struct stat *buf) return ret; } - return(real_lxstat(ver, path, buf)); + return real_lxstat(ver, path, buf); } int (*real_xstat)(int ver, __const char *path, struct stat *buf); @@ -196,37 +232,73 @@ ssize_t (*real_read)(int fd, void *buf, size_t count); ssize_t read(int fd, void *buf, size_t count) { - if ((fd & ISCSI_FD_MASK) == ISCSI_FD_MASK) { - int i; + if ((iscsi_fd_list[fd].is_iscsi == 1) && (iscsi_fd_list[fd].in_flight == 0)) { uint64_t offset; uint32_t num_blocks; struct scsi_task *task; - i = fd & ISCSI_FD_NUM; + if (iscsi_fd_list[fd].dup2fd >= 0) { + return read(iscsi_fd_list[fd].dup2fd, buf, count); + } + offset = iscsi_fd_list[fd].offset / iscsi_fd_list[fd].block_size * iscsi_fd_list[fd].block_size; + num_blocks = (iscsi_fd_list[fd].offset - offset + count + iscsi_fd_list[fd].block_size - 1) / iscsi_fd_list[fd].block_size; - offset = iscsi_fd_list[i].offset / iscsi_fd_list[i].block_size * iscsi_fd_list[i].block_size; - num_blocks = (iscsi_fd_list[i].offset - offset + count + iscsi_fd_list[i].block_size - 1) / iscsi_fd_list[i].block_size; - - - task = iscsi_read10_sync(iscsi_fd_list[i].iscsi, iscsi_fd_list[i].lun, offset / iscsi_fd_list[i].block_size, num_blocks * iscsi_fd_list[i].block_size, iscsi_fd_list[i].block_size); + iscsi_fd_list[fd].in_flight = 1; + task = iscsi_read10_sync(iscsi_fd_list[fd].iscsi, iscsi_fd_list[fd].lun, offset / iscsi_fd_list[fd].block_size, num_blocks * iscsi_fd_list[fd].block_size, iscsi_fd_list[fd].block_size); + iscsi_fd_list[fd].in_flight = 0; if (task == NULL || task->status != SCSI_STATUS_GOOD) { fprintf(stderr, "ld-iscsi: failed to send read10 command\n"); errno = EIO; return -1; } - memcpy(buf, &task->datain.data[iscsi_fd_list[i].offset - offset], count); - iscsi_fd_list[i].offset += count; + memcpy(buf, &task->datain.data[iscsi_fd_list[fd].offset - offset], count); + iscsi_fd_list[fd].offset += count; scsi_free_scsi_task(task); + return count; } - return(real_read(fd, buf, count)); + return real_read(fd, buf, count); } + +int (*real_dup2)(int oldfd, int newfd); + +int dup2(int oldfd, int newfd) +{ + close(newfd); + + if (iscsi_fd_list[oldfd].is_iscsi == 1) { + int ret; + if (iscsi_fd_list[oldfd].dup2fd >= 0) { + return dup2(iscsi_fd_list[oldfd].dup2fd, newfd); + } + + ret = real_dup2(oldfd, newfd); + if (ret < 0) { + return ret; + } + + iscsi_fd_list[newfd].is_iscsi = 1; + iscsi_fd_list[newfd].dup2fd = oldfd; + + return newfd; + } + + return real_dup2(oldfd, newfd); +} + + void _init(void) { + int i; + + for(i = 0; i < ISCSI_MAX_FD; i++) { + iscsi_fd_list[i].dup2fd = -1; + } + real_open = dlsym(RTLD_NEXT, "open"); if (real_open == NULL) { fprintf(stderr, "ld_iscsi: Failed to dlsym(open)\n"); @@ -261,4 +333,10 @@ void _init(void) fprintf(stderr, "ld_iscsi: Failed to dlsym(read)\n"); exit(10); } + + real_dup2 = dlsym(RTLD_NEXT, "dup2"); + if (real_dup2 == NULL) { + fprintf(stderr, "ld_iscsi: Failed to dlsym(dup2)\n"); + exit(10); + } }