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.
This commit is contained in:
Ronnie Sahlberg
2011-02-05 21:21:52 +11:00
parent eba0a2de81
commit 35821830d6
3 changed files with 297 additions and 1 deletions

View File

@@ -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

28
README
View File

@@ -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.

264
src/ld_iscsi.c Normal file
View File

@@ -0,0 +1,264 @@
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include "iscsi.h"
#include "scsi-lowlevel.h"
#include <sys/syscall.h>
#include <dlfcn.h>
#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);
}
}