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

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);
}
}