From 35821830d60840f86a084c3e3641174bd55e0ed6 Mon Sep 17 00:00:00 2001 From: Ronnie Sahlberg Date: Sat, 5 Feb 2011 21:21:52 +1100 Subject: [PATCH] LD_ISCSI.SO LD_PRELOAD hack ld_iscsi.so is a small LD_PRELOAD hack that can be used to make normal unix utilities such as 'stat' and 'cat' become iSCSI 'aware' and fake handling an iSCSI URL as a normal read-only file. See README for examples. --- Makefile.in | 6 +- README | 28 ++++++ src/ld_iscsi.c | 264 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 297 insertions(+), 1 deletion(-) create mode 100644 src/ld_iscsi.c diff --git a/Makefile.in b/Makefile.in index 59fba85..8ac809e 100644 --- a/Makefile.in +++ b/Makefile.in @@ -12,7 +12,11 @@ LIBISCSI_SO_NAME=libiscsi.so.1 VERSION=1.0.0 LIBISCSI_SO=libiscsi.so.$(VERSION) -all: bin/iscsi-inq bin/iscsi-ls lib/$(LIBISCSI_SO) +all: bin/iscsi-inq bin/iscsi-ls lib/$(LIBISCSI_SO) bin/ld_iscsi.so + +bin/ld_iscsi.so: src/ld_iscsi.o lib/libiscsi.a + mkdir -p bin + ld -shared -o $@ src/ld_iscsi.o lib/libiscsi.a -ldl bin/iscsi-ls: src/iscsi-ls.c lib/libiscsi.a mkdir -p bin diff --git a/README b/README index 8d5597f..53d993e 100644 --- a/README +++ b/README @@ -72,3 +72,31 @@ Header digest is to be used or not. This can be overridden by an application by calling iscsi_set_header_digest() if the application wants to force a specific setting. + +LD_PRELOAD FUN +============== +There is a small LD_PRELOAD hack in the src directory that intercepts a handful +of system calls and converts ISCSI URLs to look and behave as if they are +normal read-only files. +This allows using standard unix tools to become iscsi aware with no modifications. + +For example: +The stat command: this shows the size of the iSCSI LUN as if it was a normal file: +$ LD_PRELOAD=./bin/ld_iscsi.so stat iscsi://127.0.0.1:3262/iqn.ronnie.test/2 + File: `iscsi://127.0.0.1:3262/iqn.ronnie.test/2' + Size: 3431540736 Blocks: 0 IO Block: 0 regular file +Device: 0h/0d Inode: 0 Links: 0 +Access: (0444/-r--r--r--) Uid: ( 0/ root) Gid: ( 0/ root) +Access: 1970-01-01 10:00:00.000000000 +1000 +Modify: 1970-01-01 10:00:00.000000000 +1000 +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 + + +The LD_PRELOAD hack is incomplete and needs more functions to be intercepted before becomming fully functional. Patches welcome! + +For now, it is sufficiently complete for trivial commands like stat and cat. +You probably need to implement at least lseek, pread, pwrite before it becomes really useful. + diff --git a/src/ld_iscsi.c b/src/ld_iscsi.c new file mode 100644 index 0000000..5a062a0 --- /dev/null +++ b/src/ld_iscsi.c @@ -0,0 +1,264 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include + +#include "iscsi.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"; + +struct iscsi_fd_list { + struct iscsi_context *iscsi; + int lun; + uint32_t block_size; + uint64_t num_blocks; + off_t offset; +}; + +static struct iscsi_fd_list iscsi_fd_list[ISCSI_FD_NUM]; + +int (*real_open)(__const char *path, int flags, mode_t mode); + +int open(const char *path, int flags, mode_t mode) +{ + if (!strncmp(path, "iscsi:", 6)) { + 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) { + 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); + 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; + 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); + + if (iscsi_url->user != NULL) { + if (iscsi_set_initiator_username_pwd(iscsi_fd_list[i].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; + 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)); + iscsi_destroy_url(iscsi_url); + iscsi_destroy_context(iscsi_fd_list[i].iscsi); + iscsi_fd_list[i].iscsi = NULL; + errno = EIO; + return -1; + } + + task = iscsi_readcapacity10_sync(iscsi_fd_list[i].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; + errno = EIO; + return -1; + } + + rc10 = scsi_datain_unmarshall(task); + if (rc10 == NULL) { + 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; + 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; + + scsi_free_scsi_task(task); + iscsi_destroy_url(iscsi_url); + + return ISCSI_FD_MASK | i; + } + + return(real_open(path, flags, mode)); +} + +int (*real_close)(int fd); + +int close(int fd) +{ + if ((fd & ISCSI_FD_MASK) == ISCSI_FD_MASK) { + int i; + + i = fd & ISCSI_FD_NUM; + + iscsi_destroy_context(iscsi_fd_list[i].iscsi); + iscsi_fd_list[i].iscsi = NULL; + + return 0; + } + + 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; + + 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; + + return 0; + } + + return(real_fxstat(ver, fd, buf)); +} + + +int (*real_lxstat)(int ver, __const char *path, struct stat *buf); + +int __lxstat(int ver, const char *path, struct stat *buf) +{ + if (!strncmp(path, "iscsi:", 6)) { + int fd, ret; + + fd = open(path, 0, 0); + if (fd == -1) { + return fd; + } + + ret = __fxstat(ver, fd, buf); + close(fd); + return ret; + } + + return(real_lxstat(ver, path, buf)); +} + +int (*real_xstat)(int ver, __const char *path, struct stat *buf); + +int __xstat(int ver, const char *path, struct stat *buf) +{ + return __lxstat(ver, path, buf); +} + +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; + uint64_t offset; + uint32_t num_blocks; + struct scsi_task *task; + + i = fd & ISCSI_FD_NUM; + + 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); + 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; + + scsi_free_scsi_task(task); + return count; + } + + return(real_read(fd, buf, count)); +} + +void _init(void) +{ + real_open = dlsym(RTLD_NEXT, "open"); + if (real_open == NULL) { + fprintf(stderr, "ld_iscsi: Failed to dlsym(open)\n"); + exit(10); + } + + real_close = dlsym(RTLD_NEXT, "close"); + if (real_close == NULL) { + fprintf(stderr, "ld_iscsi: Failed to dlsym(close)\n"); + exit(10); + } + + real_fxstat = dlsym(RTLD_NEXT, "__fxstat"); + if (real_fxstat == NULL) { + fprintf(stderr, "ld_iscsi: Failed to dlsym(__fxstat)\n"); + exit(10); + } + + real_lxstat = dlsym(RTLD_NEXT, "__lxstat"); + if (real_lxstat == NULL) { + fprintf(stderr, "ld_iscsi: Failed to dlsym(__lxstat)\n"); + exit(10); + } + real_xstat = dlsym(RTLD_NEXT, "__xstat"); + if (real_xstat == NULL) { + fprintf(stderr, "ld_iscsi: Failed to dlsym(__xstat)\n"); + exit(10); + } + + real_read = dlsym(RTLD_NEXT, "read"); + if (real_read == NULL) { + fprintf(stderr, "ld_iscsi: Failed to dlsym(read)\n"); + exit(10); + } +}