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:
@@ -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
28
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.
|
||||
|
||||
|
||||
264
src/ld_iscsi.c
Normal file
264
src/ld_iscsi.c
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user