Commit a40b318e authored by Rusty Russell's avatar Rusty Russell

Merge branch 'ronnie'

parents 7a163ea2 1c005e12
......@@ -527,9 +527,8 @@ static bool huge_allocated(struct header *head, unsigned long offset)
}
/* They want something really big. Aim for contiguous pages (slow). */
static COLD_ATTRIBUTE
void *huge_alloc(void *pool, unsigned long poolsize,
unsigned long size, unsigned long align)
static COLD void *huge_alloc(void *pool, unsigned long poolsize,
unsigned long size, unsigned long align)
{
struct header *head = pool;
struct huge_alloc *ha;
......@@ -647,7 +646,7 @@ done:
return (char *)pool + ha->off;
}
static COLD_ATTRIBUTE void
static COLD void
huge_free(struct header *head, unsigned long poolsize, void *free)
{
unsigned long i, off, pgnum, free_off = (char *)free - (char *)head;
......@@ -687,8 +686,7 @@ huge_free(struct header *head, unsigned long poolsize, void *free)
alloc_free(head, poolsize, ha);
}
static COLD_ATTRIBUTE unsigned long
huge_size(struct header *head, void *p)
static COLD unsigned long huge_size(struct header *head, void *p)
{
unsigned long i, off = (char *)p - (char *)head;
struct huge_alloc *ha;
......
......@@ -6,16 +6,18 @@
* compiler - macros for common compiler extensions
*
* Abstracts away some compiler hints. Currently these include:
* - COLD_ATTRIBUTE
* - COLD
* For functions not called in fast paths (aka. cold functions)
* - PRINTF_ATTRIBUTE
* - PRINTF_FMT
* For functions which take printf-style parameters.
* - IDEMPOTENT_ATTRIBUTE
* - IDEMPOTENT
* For functions which return the same value for same parameters.
* - NEEDED_ATTRIBUTE
* - NEEDED
* For functions and variables which must be emitted even if unused.
* - UNNEEDED_ATTRIBUTE
* - UNNEEDED
* For functions and variables which need not be emitted if unused.
* - UNUSED
* For parameters which are not used.
* - IS_COMPILE_CONSTANT
* For using different tradeoffs for compiletime vs runtime evaluation.
*
......@@ -29,7 +31,7 @@
*
* // Example of a (slow-path) logging function.
* static int log_threshold = 2;
* static void COLD_ATTRIBUTE PRINTF_ATTRIBUTE(2,3)
* static void COLD PRINTF_FMT(2,3)
* logger(int level, const char *fmt, ...)
* {
* va_list ap;
......
......@@ -4,95 +4,111 @@
#if HAVE_ATTRIBUTE_COLD
/**
* COLD_ATTRIBUTE - a function is unlikely to be called.
* COLD - a function is unlikely to be called.
*
* Used to mark an unlikely code path and optimize appropriately.
* It is usually used on logging or error routines.
*
* Example:
* static void COLD_ATTRIBUTE moan(const char *reason)
* static void COLD moan(const char *reason)
* {
* fprintf(stderr, "Error: %s (%s)\n", reason, strerror(errno));
* }
*/
#define COLD_ATTRIBUTE __attribute__((cold))
#define COLD __attribute__((cold))
#else
#define COLD_ATTRIBUTE
#define COLD
#endif
#if HAVE_ATTRIBUTE_PRINTF
/**
* PRINTF_ATTRIBUTE - a function takes printf-style arguments
* PRINTF_FMT - a function takes printf-style arguments
* @nfmt: the 1-based number of the function's format argument.
* @narg: the 1-based number of the function's first variable argument.
*
* This allows the compiler to check your parameters as it does for printf().
*
* Example:
* void PRINTF_ATTRIBUTE(2,3) my_printf(const char *prefix,
* const char *fmt, ...);
* void PRINTF_FMT(2,3) my_printf(const char *prefix, const char *fmt, ...);
*/
#define PRINTF_ATTRIBUTE(nfmt, narg) \
#define PRINTF_FMT(nfmt, narg) \
__attribute__((format(__printf__, nfmt, narg)))
#else
#define PRINTF_ATTRIBUTE(nfmt, narg)
#define PRINTF_FMT(nfmt, narg)
#endif
#if HAVE_ATTRIBUTE_CONST
/**
* IDEMPOTENT_ATTRIBUTE - a function's return depends only on its argument
* IDEMPOTENT - a function's return depends only on its argument
*
* This allows the compiler to assume that the function will return the exact
* same value for the exact same arguments. This implies that the function
* must not use global variables, or dereference pointer arguments.
*/
#define IDEMPOTENT_ATTRIBUTE __attribute__((const))
#define IDEMPOTENT __attribute__((const))
#else
#define IDEMPOTENT_ATTRIBUTE
#define IDEMPOTENT
#endif
#if HAVE_ATTRIBUTE_UNUSED
/**
* UNNEEDED_ATTRIBUTE - a parameter/variable/function may not be needed
* UNNEEDED - a variable/function may not be needed
*
* This suppresses warnings about unused variables or parameters, but tells
* This suppresses warnings about unused variables or functions, but tells
* the compiler that if it is unused it need not emit it into the source code.
*
* Example:
* // With some preprocessor options, this is unnecessary.
* static UNNEEDED_ATTRIBUTE int counter;
* static UNNEEDED int counter;
*
* // With some preprocessor options, this is unnecessary.
* static UNNEEDED_ATTRIBUTE void add_to_counter(int add)
* static UNNEEDED void add_to_counter(int add)
* {
* counter += add;
* }
*/
#define UNNEEDED_ATTRIBUTE __attribute__((unused))
#define UNNEEDED __attribute__((unused))
#if HAVE_ATTRIBUTE_USED
/**
* NEEDED_ATTRIBUTE - a parameter/variable/function is needed
* NEEDED - a variable/function is needed
*
* This suppresses warnings about unused variables or parameters, but tells
* This suppresses warnings about unused variables or functions, but tells
* the compiler that it must exist even if it (seems) unused.
*
* Example:
* // Even if this is unused, these are vital for debugging.
* static UNNEEDED_ATTRIBUTE int counter;
* static UNNEEDED_ATTRIBUTE void dump_counter(void)
* static NEEDED int counter;
* static NEEDED void dump_counter(void)
* {
* printf("Counter is %i\n", counter);
* }
*/
#define NEEDED_ATTRIBUTE __attribute__((used))
#define NEEDED __attribute__((used))
#else
/* Before used, unused functions and vars were always emitted. */
#define NEEDED_ATTRIBUTE __attribute__((unused))
#define NEEDED __attribute__((unused))
#endif
/**
* UNUSED - a parameter is unused
*
* Some compilers (eg. gcc with -W or -Wunused) warn about unused
* function parameters. This suppresses such warnings and indicates
* to the reader that it's deliberate.
*
* Example:
* // This is used as a callback, so needs to have this prototype.
* static int some_callback(void *unused UNUSED)
* {
* return 0;
* }
*/
#define UNUSED __attribute__((unused))
#else
#define UNNEEDED_ATTRIBUTE
#define NEEDED_ATTRIBUTE
#define UNNEEDED
#define NEEDED
#define UNUSED
#endif
#if HAVE_BUILTIN_CONSTANT_P
......
#include <ccan/compiler/compiler.h>
static void PRINTF_ATTRIBUTE(2,3) my_printf(int x, const char *fmt, ...)
static void PRINTF_FMT(2,3) my_printf(int x, const char *fmt, ...)
{
}
......
......@@ -155,7 +155,7 @@ static void ht_add(struct htable *ht, const void *new, size_t h)
ht->table[i] = make_hval(ht, new, get_hash_ptr_bits(ht, h)|perfect);
}
static COLD_ATTRIBUTE bool double_table(struct htable *ht)
static COLD bool double_table(struct htable *ht)
{
unsigned int i;
size_t oldnum = (size_t)1 << ht->bits;
......@@ -192,7 +192,7 @@ static COLD_ATTRIBUTE bool double_table(struct htable *ht)
return true;
}
static COLD_ATTRIBUTE void rehash_table(struct htable *ht)
static COLD void rehash_table(struct htable *ht)
{
size_t start, i;
uintptr_t e;
......@@ -217,7 +217,7 @@ static COLD_ATTRIBUTE void rehash_table(struct htable *ht)
}
/* We stole some bits, now we need to put them back... */
static COLD_ATTRIBUTE void update_common(struct htable *ht, const void *p)
static COLD void update_common(struct htable *ht, const void *p)
{
unsigned int i;
uintptr_t maskdiff, bitsdiff;
......
......@@ -16,7 +16,7 @@
year=1998,
note="\url{http://supertech.csail.mit.edu/papers/debruijn.pdf}"
}*/
static UNNEEDED_ATTRIBUTE const unsigned char DEBRUIJN_IDX32[32]={
static UNNEEDED const unsigned char DEBRUIJN_IDX32[32]={
0, 1,28, 2,29,14,24, 3,30,22,20,15,25,17, 4, 8,
31,27,13,23,21,19,16, 7,26,12,18, 6,11, 5,10, 9
};
......
......@@ -24,7 +24,7 @@
* return 1U << ilog32(i-1);
* }
*/
int ilog32(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
int ilog32(uint32_t _v) IDEMPOTENT;
/**
* ilog32_nz - Integer binary logarithm of a non-zero 32-bit value.
......@@ -43,7 +43,7 @@ int ilog32(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
* return ilog32_nz(i) - 1;
* }
*/
int ilog32_nz(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
int ilog32_nz(uint32_t _v) IDEMPOTENT;
/**
* ilog64 - Integer binary logarithm of a 64-bit value.
......@@ -55,7 +55,7 @@ int ilog32_nz(uint32_t _v) IDEMPOTENT_ATTRIBUTE;
* See Also:
* ilog64_nz(), ilog32()
*/
int ilog64(uint64_t _v) IDEMPOTENT_ATTRIBUTE;
int ilog64(uint64_t _v) IDEMPOTENT;
/**
* ilog64_nz - Integer binary logarithm of a non-zero 64-bit value.
......@@ -67,7 +67,7 @@ int ilog64(uint64_t _v) IDEMPOTENT_ATTRIBUTE;
* See Also:
* ilog64(), ilog32_nz()
*/
int ilog64_nz(uint64_t _v) IDEMPOTENT_ATTRIBUTE;
int ilog64_nz(uint64_t _v) IDEMPOTENT;
/**
* STATIC_ILOG_32 - The integer logarithm of an (unsigned, 32-bit) constant.
......
../../licenses/GPL-3
\ No newline at end of file
LIBS=
CC=gcc
CFLAGS=-g -O0 -Wall -W -I../..
LIBISCSI_OBJ = socket.o init.o login.o nop.o pdu.o discovery.o scsi-command.o scsi-lowlevel.o
all: tools/iscsiclient
tools/iscsiclient: tools/iscsiclient.o libiscsi.a
$(CC) $(CFLAGS) -o $@ tools/iscsiclient.o libiscsi.a $(LIBS)
libiscsi.a: $(LIBISCSI_OBJ)
@echo Creating library $@
ar r libiscsi.a $(LIBISCSI_OBJ)
ranlib libiscsi.a
tools/iscsiclient.o: tools/iscsiclient.c
@echo Compiling $@
$(CC) $(CFLAGS) -c tools/iscsiclient.c -o $@
socket.o: socket.c iscsi.h iscsi-private.h
init.o: init.c iscsi.h iscsi-private.h
login.o: login.c iscsi.h iscsi-private.h
pdu.o: pdu.c iscsi.h iscsi-private.h
nop.o: nop.c iscsi.h iscsi-private.h
discovery.o: discovery.c iscsi.h iscsi-private.h
scsi-command.o: scsi-command.c iscsi.h iscsi-private.h
scsi-lowlevel.o: scsi-lowlevel.c scsi-lowlevel.h
clean:
rm -f tools/iscsiclient
rm -f *.o
rm -f libiscsi.a
#include <stdio.h>
#include <string.h>
/**
* iscsi - async library for iscsi functionality
*
* The iscsi module is a work in progress.
*
* It aims to become a full async library for iscsi functionality,
* including all features required to establish and maintain a iscsi
* session, as well as a low level scsi library to create scsi cdb's
* and parse/unmarshall data-in structures.
*
* License: GPL (3 or any later version)
* Author: Ronnie Sahlberg <ronniesahlberg@gmail.com>
*/
int main(int argc, char *argv[])
{
if (argc != 2)
return 1;
if (strcmp(argv[1], "depends") == 0) {
printf("ccan/compiler\n");
return 0;
}
return 1;
}
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include "iscsi.h"
#include "iscsi-private.h"
int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
{
struct iscsi_pdu *pdu;
char *str;
if (iscsi == NULL) {
printf("trying to send text on NULL context\n");
return -1;
}
if (iscsi->session_type != ISCSI_SESSION_DISCOVERY) {
printf("Trying to do discovery on non-discovery session\n");
return -2;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_TEXT_REQUEST, ISCSI_PDU_TEXT_RESPONSE);
if (pdu == NULL) {
printf("Failed to allocate text pdu\n");
return -3;
}
/* immediate */
iscsi_pdu_set_immediate(pdu);
/* flags */
iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_TEXT_FINAL);
/* target transfer tag */
iscsi_pdu_set_ttt(pdu, 0xffffffff);
/* sendtargets */
str = "SendTargets=All";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -4;
}
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
printf("failed to queue iscsi text pdu\n");
iscsi_free_pdu(iscsi, pdu);
return -5;
}
return 0;
}
static void iscsi_free_discovery_addresses(struct iscsi_discovery_address *addresses)
{
while (addresses != NULL) {
struct iscsi_discovery_address *next = addresses->next;
if (addresses->target_name != NULL) {
free(discard_const(addresses->target_name));
addresses->target_name = NULL;
}
if (addresses->target_address != NULL) {
free(discard_const(addresses->target_address));
addresses->target_address = NULL;
}
addresses->next = NULL;
free(addresses);
addresses = next;
}
}
int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
{
struct iscsi_discovery_address *targets = NULL;
/* verify the response looks sane */
if (hdr[1] != ISCSI_PDU_TEXT_FINAL) {
printf("unsupported flags in text reply %02x\n", hdr[1]);
pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
return -1;
}
/* skip past the header */
hdr += ISCSI_HEADER_SIZE;
size -= ISCSI_HEADER_SIZE;
while (size > 0) {
int len;
len = strlen((char *)hdr);
if (len == 0) {
break;
}
if (len > size) {
printf("len > size when parsing discovery data %d>%d\n", len, size);
pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
/* parse the strings */
if (!strncmp((char *)hdr, "TargetName=", 11)) {
struct iscsi_discovery_address *target;
target = malloc(sizeof(struct iscsi_discovery_address));
if (target == NULL) {
printf("Failed to allocate data for new discovered target\n");
pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
bzero(target, sizeof(struct iscsi_discovery_address));
target->target_name = strdup((char *)hdr+11);
if (target->target_name == NULL) {
printf("Failed to allocate data for new discovered target name\n");
pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
free(target);
target = NULL;
iscsi_free_discovery_addresses(targets);
return -1;
}
target->next = targets;
targets = target;
} else if (!strncmp((char *)hdr, "TargetAddress=", 14)) {
targets->target_address = strdup((char *)hdr+14);
if (targets->target_address == NULL) {
printf("Failed to allocate data for new discovered target address\n");
pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
} else {
printf("Dont know how to handle discovery string : %s\n", hdr);
pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
iscsi_free_discovery_addresses(targets);
return -1;
}
hdr += len + 1;
size -= len + 1;
}
pdu->callback(iscsi, ISCSI_STATUS_GOOD, targets, pdu->private_data);
iscsi_free_discovery_addresses(targets);
return 0;
}
/*
Unix SMB/CIFS implementation.
some simple double linked list macros
Copyright (C) Andrew Tridgell 1998-2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* To use these macros you must have a structure containing a next and
prev pointer */
#ifndef _DLINKLIST_H
#define _DLINKLIST_H
/*
February 2010 - changed list format to have a prev pointer from the
list head. This makes DLIST_ADD_END() O(1) even though we only have
one list pointer.
The scheme is as follows:
1) with no entries in the list:
list_head == NULL
2) with 1 entry in the list:
list_head->next == NULL
list_head->prev == list_head
3) with 2 entries in the list:
list_head->next == element2
list_head->prev == element2
element2->prev == list_head
element2->next == NULL
4) with N entries in the list:
list_head->next == element2
list_head->prev == elementN
elementN->prev == element{N-1}
elementN->next == NULL
This allows us to find the tail of the list by using
list_head->prev, which means we can add to the end of the list in
O(1) time
Note that the 'type' arguments below are no longer needed, but
are kept for now to prevent an incompatible argument change
*/
/*
add an element at the front of a list
*/
#define DLIST_ADD(list, p) \
do { \
if (!(list)) { \
(p)->prev = (list) = (p); \
(p)->next = NULL; \
} else { \
(p)->prev = (list)->prev; \
(list)->prev = (p); \
(p)->next = (list); \
(list) = (p); \
} \
} while (0)
/*
remove an element from a list
Note that the element doesn't have to be in the list. If it
isn't then this is a no-op
*/
#define DLIST_REMOVE(list, p) \
do { \
if ((p) == (list)) { \
if ((p)->next) (p)->next->prev = (p)->prev; \
(list) = (p)->next; \
} else if ((list) && (p) == (list)->prev) { \
(p)->prev->next = NULL; \
(list)->prev = (p)->prev; \
} else { \
if ((p)->prev) (p)->prev->next = (p)->next; \
if ((p)->next) (p)->next->prev = (p)->prev; \
} \
if ((p) != (list)) (p)->next = (p)->prev = NULL; \
} while (0)
/*
find the head of the list given any element in it.
Note that this costs O(N), so you should avoid this macro
if at all possible!
*/
#define DLIST_HEAD(p, result_head) \
do { \
(result_head) = (p); \
while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \
} while(0)
/* return the last element in the list */
#define DLIST_TAIL(list) ((list)?(list)->prev:NULL)
/* return the previous element in the list. */
#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL)
/* insert 'p' after the given element 'el' in a list. If el is NULL then
this is the same as a DLIST_ADD() */
#define DLIST_ADD_AFTER(list, p, el) \
do { \
if (!(list) || !(el)) { \
DLIST_ADD(list, p); \
} else { \
(p)->prev = (el); \
(p)->next = (el)->next; \
(el)->next = (p); \
if ((p)->next) (p)->next->prev = (p); \
if ((list)->prev == (el)) (list)->prev = (p); \
}\
} while (0)
/*
add to the end of a list.
Note that 'type' is ignored
*/
#define DLIST_ADD_END(list, p, type) \
do { \
if (!(list)) { \
DLIST_ADD(list, p); \
} else { \
DLIST_ADD_AFTER(list, p, (list)->prev); \
} \
} while (0)
/* promote an element to the from of a list */
#define DLIST_PROMOTE(list, p) \
do { \
DLIST_REMOVE(list, p); \
DLIST_ADD(list, p); \
} while (0)
/*
demote an element to the end of a list.
Note that 'type' is ignored
*/
#define DLIST_DEMOTE(list, p, type) \
do { \
DLIST_REMOVE(list, p); \
DLIST_ADD_END(list, p, NULL); \
} while (0)
/*
concatenate two lists - putting all elements of the 2nd list at the
end of the first list.
Note that 'type' is ignored
*/
#define DLIST_CONCATENATE(list1, list2, type) \
do { \
if (!(list1)) { \
(list1) = (list2); \
} else { \
(list1)->prev->next = (list2); \
if (list2) { \
void *_tmplist = (void *)(list1)->prev; \
(list1)->prev = (list2)->prev; \
(list2)->prev = _tmplist; \
} \
} \
} while (0)
#endif /* _DLINKLIST_H */
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <time.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "dlinklist.h"
struct iscsi_context *iscsi_create_context(const char *initiator_name)
{
struct iscsi_context *iscsi;
iscsi = malloc(sizeof(struct iscsi_context));
if (iscsi == NULL) {
printf("Failed to allocate iscsi context\n");
return NULL;
}
bzero(iscsi, sizeof(struct iscsi_context));
iscsi->initiator_name = strdup(initiator_name);
if (iscsi->initiator_name == NULL) {
printf("Failed to allocate initiator name context\n");
free(iscsi);
return NULL;
}
iscsi->fd = -1;
/* use a "random" isid */
srandom(getpid() ^ time(NULL));
iscsi_set_random_isid(iscsi);
return iscsi;
}
int iscsi_set_random_isid(struct iscsi_context *iscsi)
{
iscsi->isid[0] = 0x80;
iscsi->isid[1] = random()&0xff;
iscsi->isid[2] = random()&0xff;
iscsi->isid[3] = random()&0xff;
iscsi->isid[4] = 0;
iscsi->isid[5] = 0;
return 0;
}
int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias)
{
if (iscsi == NULL) {
printf("Context is NULL when adding alias\n");
return -1;
}
if (iscsi->is_loggedin != 0) {
printf("Already logged in when adding alias\n");
return -2;
}
if (iscsi->alias != NULL) {
free(discard_const(iscsi->alias));
iscsi->alias = NULL;
}
iscsi->alias = strdup(alias);
if (iscsi->alias == NULL) {
printf("Failed to allocate alias name\n");
return -3;
}
return 0;
}
int iscsi_set_targetname(struct iscsi_context *iscsi, const char *target_name)
{
if (iscsi == NULL) {
printf("Context is NULL when adding targetname\n");
return -1;
}
if (iscsi->is_loggedin != 0) {
printf("Already logged in when adding targetname\n");
return -2;
}
if (iscsi->target_name != NULL) {
free(discard_const(iscsi->target_name));
iscsi->target_name= NULL;
}
iscsi->target_name = strdup(target_name);
if (iscsi->target_name == NULL) {
printf("Failed to allocate target name\n");
return -3;
}
return 0;
}
int iscsi_destroy_context(struct iscsi_context *iscsi)
{
struct iscsi_pdu *pdu;
if (iscsi == NULL) {
return 0;
}
if (iscsi->initiator_name != NULL) {
free(discard_const(iscsi->initiator_name));
iscsi->initiator_name = NULL;
}
if (iscsi->alias != NULL) {
free(discard_const(iscsi->alias));
iscsi->alias = NULL;
}
if (iscsi->is_loggedin != 0) {
printf("deswtroying context while logged in\n");
}
if (iscsi->fd != -1) {
iscsi_disconnect(iscsi);
}
if (iscsi->inbuf != NULL) {
free(iscsi->inbuf);
iscsi->inbuf = NULL;
iscsi->insize = 0;
iscsi->inpos = 0;
}
while ((pdu = iscsi->outqueue)) {
DLIST_REMOVE(iscsi->outqueue, pdu);
pdu->callback(iscsi, ISCSI_STATUS_CANCELLED, NULL, pdu->private_data);
iscsi_free_pdu(iscsi, pdu);
}
while ((pdu = iscsi->waitpdu)) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
pdu->callback(iscsi, ISCSI_STATUS_CANCELLED, NULL, pdu->private_data);
iscsi_free_pdu(iscsi, pdu);
}
free(iscsi);
return 0;
}
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CCAN_ISCSI_PRIVATE_H
#define CCAN_ISCSI_PRIVATE_H
#include <stdint.h>
#ifndef discard_const
#define discard_const(ptr) ((void *)((intptr_t)(ptr)))
#endif
struct iscsi_context {
const char *initiator_name;
const char *target_name;
const char *alias;
enum iscsi_session_type session_type;
unsigned char isid[6];
uint32_t itt;
uint32_t cmdsn;
uint32_t statsn;
int fd;
int is_connected;
int is_loggedin;
iscsi_command_cb connect_cb;
void *connect_data;
struct iscsi_pdu *outqueue;
struct iscsi_pdu *waitpdu;
int insize;
int inpos;
unsigned char *inbuf;
};
#define ISCSI_HEADER_SIZE 48
#define ISCSI_PDU_IMMEDIATE 0x40
#define ISCSI_PDU_TEXT_FINAL 0x80
#define ISCSI_PDU_TEXT_CONTINUE 0x40
#define ISCSI_PDU_LOGIN_TRANSIT 0x80
#define ISCSI_PDU_LOGIN_CONTINUE 0x40
#define ISCSI_PDU_LOGIN_CSG_SECNEG 0x00
#define ISCSI_PDU_LOGIN_CSG_OPNEG 0x04
#define ISCSI_PDU_LOGIN_CSG_FF 0x0c
#define ISCSI_PDU_LOGIN_NSG_SECNEG 0x00
#define ISCSI_PDU_LOGIN_NSG_OPNEG 0x01
#define ISCSI_PDU_LOGIN_NSG_FF 0x03
#define ISCSI_PDU_SCSI_FINAL 0x80
#define ISCSI_PDU_SCSI_READ 0x40
#define ISCSI_PDU_SCSI_WRITE 0x20
#define ISCSI_PDU_SCSI_ATTR_UNTAGGED 0x00
#define ISCSI_PDU_SCSI_ATTR_SIMPLE 0x01
#define ISCSI_PDU_SCSI_ATTR_ORDERED 0x02
#define ISCSI_PDU_SCSI_ATTR_HEADOFQUEUE 0x03
#define ISCSI_PDU_SCSI_ATTR_ACA 0x04
#define ISCSI_PDU_DATA_FINAL 0x80
#define ISCSI_PDU_DATA_ACK_REQUESTED 0x40
#define ISCSI_PDU_DATA_BIDIR_OVERFLOW 0x10
#define ISCSI_PDU_DATA_BIDIR_UNDERFLOW 0x08
#define ISCSI_PDU_DATA_RESIDUAL_OVERFLOW 0x04
#define ISCSI_PDU_DATA_RESIDUAL_UNDERFLOW 0x02
#define ISCSI_PDU_DATA_CONTAINS_STATUS 0x01
enum iscsi_opcode {ISCSI_PDU_NOP_OUT=0x00,
ISCSI_PDU_SCSI_REQUEST=0x01,
ISCSI_PDU_LOGIN_REQUEST=0x03,
ISCSI_PDU_TEXT_REQUEST=0x04,
ISCSI_PDU_LOGOUT_REQUEST=0x06,
ISCSI_PDU_NOP_IN=0x20,
ISCSI_PDU_SCSI_RESPONSE=0x21,
ISCSI_PDU_LOGIN_RESPONSE=0x23,
ISCSI_PDU_TEXT_RESPONSE=0x24,
ISCSI_PDU_DATA_IN=0x25,
ISCSI_PDU_LOGOUT_RESPONSE=0x26};
struct iscsi_pdu {
struct iscsi_pdu *prev, *next;
uint32_t itt;
uint32_t cmdsn;
enum iscsi_opcode response_opcode;
iscsi_command_cb callback;
void *private_data;
int written;
struct iscsi_data outdata;
struct iscsi_data indata;
struct iscsi_scsi_cbdata *scsi_cbdata;
};
void iscsi_free_scsi_cbdata(struct iscsi_scsi_cbdata *scsi_cbdata);
struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode);
void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags);
void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu);
void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt);
void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn);
void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun);
void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn);
void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen);
int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize);
int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu);
int iscsi_add_data(struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment);
int iscsi_set_random_isid(struct iscsi_context *iscsi);
struct scsi_task;
void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task);
int iscsi_get_pdu_size(const unsigned char *hdr);
int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, int size);
int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
int iscsi_process_text_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
int iscsi_process_scsi_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
int iscsi_process_scsi_data_in(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size, int *is_finished);
int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size);
#endif /* CCAN_ISCSI_PRIVATE_H */
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CCAN_ISCSI_H
#define CCAN_ISCSI_H
struct iscsi_context;
struct sockaddr;
/*
* Returns the file descriptor that libiscsi uses.
*/
int iscsi_get_fd(struct iscsi_context *iscsi);
/*
* Returns which events that we need to poll for for the iscsi file descriptor.
*/
int iscsi_which_events(struct iscsi_context *iscsi);
/*
* Called to process the events when events become available for the iscsi file descriptor.
*/
int iscsi_service(struct iscsi_context *iscsi, int revents);
/*
* Create a context for an ISCSI session.
* Initiator_name is the iqn name we want to identify to the target as.
*
* Returns:
* 0: success
* <0: error
*/
struct iscsi_context *iscsi_create_context(const char *initiator_name);
/*
* Destroy an existing ISCSI context and tear down any existing connection.
* Callbacks for any command in flight will be invoked with ISCSI_STATUS_CANCELLED.
*
* Returns:
* 0: success
* <0: error
*/
int iscsi_destroy_context(struct iscsi_context *iscsi);
/*
* Set an optional alias name to identify with when connecting to the target
*
* Returns:
* 0: success
* <0: error
*/
int iscsi_set_alias(struct iscsi_context *iscsi, const char *alias);
/*
* Set the iqn name of the taqget to login to.
* The target name must be set before a normal-login can be initiated.
* Only discovery-logins are possible without setting the target iqn name.
*
* Returns:
* 0: success
* <0: error
*/
int iscsi_set_targetname(struct iscsi_context *iscsi, const char *targetname);
/* Types of icsi sessions. Discovery sessions are used to query for what targets exist behind
* the portal connected to. Normal sessions are used to log in and do I/O to the SCSI LUNs
*/
enum iscsi_session_type {ISCSI_SESSION_DISCOVERY=1, ISCSI_SESSION_NORMAL=2};
/*
* Set the session type for a scsi context.
* Session type can only be set/changed while the iscsi context is not logged in to
* a target.
*
* Returns:
* 0: success
* <0: error
*/
int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type);
/* ISCSI_STATUS_GOOD must map to SCSI_STATUS_GOOD
* and ISCSI_STATUS_CHECK_CONDITION must map to SCSI_STATUS_CHECK_CONDITION
*/
enum icsi_status { ISCSI_STATUS_GOOD =0,
ISCSI_STATUS_CHECK_CONDITION =2,
ISCSI_STATUS_CANCELLED =0x0f000000,
ISCSI_STATUS_ERROR =0x0f000001 };
/*
* Generic callback for completion of iscsi_*_async().
* command_data depends on status.
*/
typedef void (*iscsi_command_cb)(struct iscsi_context *iscsi, int status, void *command_data, void *private_data);
/*
* Asynchronous call to connect a TCP connection to the target-host/port
*
* Returns:
* 0 if the call was initiated and a connection will be attempted. Result of the connection will be reported
* through the callback function.
* <0 if there was an error. The callback function will not be invoked.
*
* This command is unique in that the callback can be invoked twice.
*
* Callback parameters :
* status can be either of :
* ISCSI_STATUS_GOOD : Connection was successful. Command_data is NULL.
* In this case the callback will be invoked a second time once the connection
* is torn down.
*
* ISCSI_STATUS_ERROR : Either failed to establish the connection, or an already established connection
* has failed with an error.
*
* The callback will NOT be invoked if the session is explicitely torn down through a call to
* iscsi_disconnect() or iscsi_destroy_context().
*/
int iscsi_connect_async(struct iscsi_context *iscsi, const char *target, iscsi_command_cb cb, void *private_data);
/*
* Disconnect a connection to a target.
* You can not disconnect while being logged in to a target.
*
* Returns:
* 0 disconnect was successful
* <0 error
*/
int iscsi_disconnect(struct iscsi_context *iscsi);
/*
* Asynchronous call to perform an ISCSI login.
*
* Returns:
* 0 if the call was initiated and a login will be attempted. Result of the login will be reported
* through the callback function.
* <0 if there was an error. The callback function will not be invoked.
*
* Callback parameters :
* status can be either of :
* ISCSI_STATUS_GOOD : login was successful. Command_data is always NULL.
* ISCSI_STATUS_CANCELLED: login was aborted. Command_data is NULL.
* ISCSI_STATUS_ERROR : login failed. Command_data is NULL.
*/
int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
/*
* Asynchronous call to perform an ISCSI logout.
*
* Returns:
* 0 if the call was initiated and a logout will be attempted. Result of the logout will be reported
* through the callback function.
* <0 if there was an error. The callback function will not be invoked.
*
* Callback parameters :
* status can be either of :
* ISCSI_STATUS_GOOD : logout was successful. Command_data is always NULL.
* ISCSI_STATUS_CANCELLED: logout was aborted. Command_data is NULL.
*/
int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
/*
* Asynchronous call to perform an ISCSI discovery.
*
* discoveries can only be done on connected and logged in discovery sessions.
*
* Returns:
* 0 if the call was initiated and a discovery will be attempted. Result of the logout will be reported
* through the callback function.
* <0 if there was an error. The callback function will not be invoked.
*
* Callback parameters :
* status can be either of :
* ISCSI_STATUS_GOOD : Discovery was successful. Command_data is a pointer to a
* iscsi_discovery_address list of structures.
* This list of structures is only valid for the duration of the
* callback and all data will be freed once the callback returns.
* ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
*/
int iscsi_discovery_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data);
struct iscsi_discovery_address {
struct iscsi_discovery_address *next;
const char *target_name;
const char *target_address;
};
/*
* Asynchronous call to perform an ISCSI NOP-OUT call
*
* Returns:
* 0 if the call was initiated and a nop-out will be attempted. Result will be reported
* through the callback function.
* <0 if there was an error. The callback function will not be invoked.
*
* Callback parameters :
* status can be either of :
* ISCSI_STATUS_GOOD : NOP-OUT was successful and the server responded with a NOP-IN
* callback_data is a iscsi_data structure containing the data returned from
* the server.
* ISCSI_STATUS_CANCELLED: Discovery was aborted. Command_data is NULL.
*/
int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data);
/* These are the possible status values for the callbacks for scsi commands.
* The content of command_data depends on the status type.
*
* status :
* ISCSI_STATUS_GOOD the scsi command completed successfullt on the target.
* If this scsi command returns DATA-IN, that data is stored in an scsi_task structure
* returned in the command_data parameter. This buffer will be automatically freed once the callback
* returns.
*
* ISCSI_STATUS_CHECK_CONDITION the scsi command failed with a scsi sense.
* Command_data contains a struct scsi_task. When the callback returns, this buffer
* will automatically become freed.
*
* ISCSI_STATUS_CANCELLED the scsi command was aborted. Command_data is NULL.
*
* ISCSI_STATUS_ERROR the command failed. Command_data is NULL.
*/
struct iscsi_data {
int size;
unsigned char *data;
};
/*
* Async commands for SCSI
*/
int iscsi_reportluns_async(struct iscsi_context *iscsi, iscsi_command_cb cb, int report_type, int alloc_len, void *private_data);
int iscsi_testunitready_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, void *private_data);
int iscsi_inquiry_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int evpd, int page_code, int maxsize, void *private_data);
int iscsi_readcapacity10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int pmi, void *private_data);
int iscsi_read10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int lba, int datalen, int blocksize, void *private_data);
int iscsi_write10_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, unsigned char *data, int datalen, int lba, int fua, int fuanv, int blocksize, void *private_data);
int iscsi_modesense6_async(struct iscsi_context *iscsi, int lun, iscsi_command_cb cb, int dbd, int pc, int page_code, int sub_page_code, unsigned char alloc_len, void *private_data);
#endif /* CCAN_ISCSI_H */
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <arpa/inet.h>
#include <ccan/compiler/compiler.h>
#include "iscsi.h"
#include "iscsi-private.h"
int iscsi_login_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
{
struct iscsi_pdu *pdu;
char *str;
int ret;
if (iscsi == NULL) {
printf("trying to login on NULL context\n");
return -1;
}
if (iscsi->is_loggedin != 0) {
printf("trying to login while already logged in\n");
return -2;
}
switch (iscsi->session_type) {
case ISCSI_SESSION_DISCOVERY:
case ISCSI_SESSION_NORMAL:
break;
default:
printf("trying to login without setting session type\n");
return -3;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGIN_REQUEST, ISCSI_PDU_LOGIN_RESPONSE);
if (pdu == NULL) {
printf("Failed to allocate login pdu\n");
return -4;
}
/* login request */
iscsi_pdu_set_immediate(pdu);
/* flags */
iscsi_pdu_set_pduflags(pdu, ISCSI_PDU_LOGIN_TRANSIT|ISCSI_PDU_LOGIN_CSG_OPNEG|ISCSI_PDU_LOGIN_NSG_FF);
/* initiator name */
if (asprintf(&str, "InitiatorName=%s", iscsi->initiator_name) == -1) {
printf("asprintf failed\n");
iscsi_free_pdu(iscsi, pdu);
return -5;
}
ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
free(str);
if (ret != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -6;
}
/* optional alias */
if (iscsi->alias) {
if (asprintf(&str, "InitiatorAlias=%s", iscsi->alias) == -1) {
printf("asprintf failed\n");
iscsi_free_pdu(iscsi, pdu);
return -7;
}
ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
free(str);
if (ret != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -8;
}
}
/* target name */
if (iscsi->session_type == ISCSI_SESSION_NORMAL) {
if (iscsi->target_name == NULL) {
printf("trying normal connect but target name not set\n");
iscsi_free_pdu(iscsi, pdu);
return -9;
}
if (asprintf(&str, "TargetName=%s", iscsi->target_name) == -1) {
printf("asprintf failed\n");
iscsi_free_pdu(iscsi, pdu);
return -10;
}
ret = iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1);
free(str);
if (ret != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -11;
}
}
/* session type */
switch (iscsi->session_type) {
case ISCSI_SESSION_DISCOVERY:
str = "SessionType=Discovery";
break;
case ISCSI_SESSION_NORMAL:
str = "SessionType=Normal";
break;
default:
printf("can not handle sessions %d yet\n", iscsi->session_type);
return -12;
}
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -13;
}
str = "HeaderDigest=None";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -14;
}
str = "DataDigest=None";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -15;
}
str = "InitialR2T=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -16;
}
str = "ImmediateData=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -17;
}
str = "MaxBurstLength=262144";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -18;
}
str = "FirstBurstLength=262144";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -19;
}
str = "MaxRecvDataSegmentLength=262144";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -20;
}
str = "DataPDUInOrder=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -21;
}
str = "DataSequenceInOrder=Yes";
if (iscsi_pdu_add_data(iscsi, pdu, (unsigned char *)str, strlen(str)+1) != 0) {
printf("pdu add data failed\n");
iscsi_free_pdu(iscsi, pdu);
return -22;
}
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
printf("failed to queue iscsi login pdu\n");
iscsi_free_pdu(iscsi, pdu);
return -23;
}
return 0;
}
int iscsi_process_login_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
{
int status;
if (size < ISCSI_HEADER_SIZE) {
printf("dont have enough data to read status from login reply\n");
return -1;
}
/* XXX here we should parse the data returned in case the target renegotiated some
* some parameters.
* we should also do proper handshaking if the target is not yet prepared to transition
* to the next stage
*/
status = ntohs(*(uint16_t *)&hdr[36]);
if (status != 0) {
pdu->callback(iscsi, ISCSI_STATUS_ERROR, NULL, pdu->private_data);
return 0;
}
iscsi->statsn = ntohs(*(uint16_t *)&hdr[24]);
iscsi->is_loggedin = 1;
pdu->callback(iscsi, ISCSI_STATUS_GOOD, NULL, pdu->private_data);
return 0;
}
int iscsi_logout_async(struct iscsi_context *iscsi, iscsi_command_cb cb, void *private_data)
{
struct iscsi_pdu *pdu;
if (iscsi == NULL) {
printf("trying to logout on NULL context\n");
return -1;
}
if (iscsi->is_loggedin == 0) {
printf("trying to logout while not logged in\n");
return -2;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_LOGOUT_REQUEST, ISCSI_PDU_LOGOUT_RESPONSE);
if (pdu == NULL) {
printf("Failed to allocate logout pdu\n");
return -3;
}
/* logout request has the immediate flag set */
iscsi_pdu_set_immediate(pdu);
/* flags : close the session */
iscsi_pdu_set_pduflags(pdu, 0x80);
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
printf("failed to queue iscsi logout pdu\n");
iscsi_free_pdu(iscsi, pdu);
return -4;
}
return 0;
}
int iscsi_process_logout_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size UNUSED)
{
iscsi->is_loggedin = 0;
pdu->callback(iscsi, ISCSI_STATUS_GOOD, NULL, pdu->private_data);
return 0;
}
int iscsi_set_session_type(struct iscsi_context *iscsi, enum iscsi_session_type session_type)
{
if (iscsi == NULL) {
printf("Trying to set sesssion type on NULL context\n");
return -1;
}
if (iscsi->is_loggedin) {
printf("trying to set session type while logged in\n");
return -2;
}
iscsi->session_type = session_type;
return 0;
}
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include "iscsi.h"
#include "iscsi-private.h"
int iscsi_nop_out_async(struct iscsi_context *iscsi, iscsi_command_cb cb, unsigned char *data, int len, void *private_data)
{
struct iscsi_pdu *pdu;
if (iscsi == NULL) {
printf("trying to send nop-out on NULL context\n");
return -1;
}
if (iscsi->is_loggedin == 0) {
printf("trying send nop-out while not logged in\n");
return -2;
}
pdu = iscsi_allocate_pdu(iscsi, ISCSI_PDU_NOP_OUT, ISCSI_PDU_NOP_IN);
if (pdu == NULL) {
printf("Failed to allocate nop-out pdu\n");
return -3;
}
/* immediate flag */
iscsi_pdu_set_immediate(pdu);
/* flags */
iscsi_pdu_set_pduflags(pdu, 0x80);
/* ttt */
iscsi_pdu_set_ttt(pdu, 0xffffffff);
/* lun */
iscsi_pdu_set_lun(pdu, 2);
/* cmdsn is not increased if Immediate delivery*/
iscsi_pdu_set_cmdsn(pdu, iscsi->cmdsn);
pdu->cmdsn = iscsi->cmdsn;
// iscsi->cmdsn++;
pdu->callback = cb;
pdu->private_data = private_data;
if (iscsi_pdu_add_data(iscsi, pdu, data, len) != 0) {
printf("Failed to add outdata to nop-out\n");
iscsi_free_pdu(iscsi, pdu);
return -4;
}
if (iscsi_queue_pdu(iscsi, pdu) != 0) {
printf("failed to queue iscsi nop-out pdu\n");
iscsi_free_pdu(iscsi, pdu);
return -5;
}
return 0;
}
int iscsi_process_nop_out_reply(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, const unsigned char *hdr, int size)
{
struct iscsi_data data;
data.data = NULL;
data.size = 0;
if (size > ISCSI_HEADER_SIZE) {
data.data = discard_const(&hdr[ISCSI_HEADER_SIZE]);
data.size = size - ISCSI_HEADER_SIZE;
}
pdu->callback(iscsi, ISCSI_STATUS_GOOD, &data, pdu->private_data);
return 0;
}
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <string.h>
#include <arpa/inet.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "scsi-lowlevel.h"
#include "dlinklist.h"
struct iscsi_pdu *iscsi_allocate_pdu(struct iscsi_context *iscsi, enum iscsi_opcode opcode, enum iscsi_opcode response_opcode)
{
struct iscsi_pdu *pdu;
pdu = malloc(sizeof(struct iscsi_pdu));
if (pdu == NULL) {
printf("failed to allocate pdu\n");
return NULL;
}
bzero(pdu, sizeof(struct iscsi_pdu));
pdu->outdata.size = ISCSI_HEADER_SIZE;
pdu->outdata.data = malloc(pdu->outdata.size);
if (pdu->outdata.data == NULL) {
printf("failed to allocate pdu header\n");
free(pdu);
pdu = NULL;
return NULL;
}
bzero(pdu->outdata.data, pdu->outdata.size);
/* opcode */
pdu->outdata.data[0] = opcode;
pdu->response_opcode = response_opcode;
/* isid */
if (opcode ==ISCSI_PDU_LOGIN_REQUEST) {
memcpy(&pdu->outdata.data[8], &iscsi->isid[0], 6);
}
/* itt */
*(uint32_t *)&pdu->outdata.data[16] = htonl(iscsi->itt);
pdu->itt = iscsi->itt;
iscsi->itt++;
return pdu;
}
void iscsi_free_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
if (pdu == NULL) {
printf("trying to free NULL pdu\n");
return;
}
if (pdu->outdata.data) {
free(pdu->outdata.data);
pdu->outdata.data = NULL;
}
if (pdu->indata.data) {
free(pdu->indata.data);
pdu->indata.data = NULL;
}
if (pdu->scsi_cbdata) {
iscsi_free_scsi_cbdata(pdu->scsi_cbdata);
pdu->scsi_cbdata = NULL;
}
free(pdu);
}
int iscsi_add_data(struct iscsi_data *data, unsigned char *dptr, int dsize, int pdualignment)
{
int len, aligned;
unsigned char *buf;
if (dsize == 0) {
printf("Trying to append zero size data to iscsi_data\n");
return -1;
}
len = data->size + dsize;
aligned = len;
if (pdualignment) {
aligned = (aligned+3)&0xfffffffc;
}
buf = malloc(aligned);
if (buf == NULL) {
printf("failed to allocate buffer for %d bytes\n", len);
return -2;
}
memcpy(buf, data->data, data->size);
memcpy(buf + data->size, dptr, dsize);
if (len != aligned) {
/* zero out any padding at the end */
bzero(buf+len, aligned-len);
}
free(data->data);
data->data = buf;
data->size = len;
return 0;
}
int iscsi_pdu_add_data(struct iscsi_context *iscsi, struct iscsi_pdu *pdu, unsigned char *dptr, int dsize)
{
if (pdu == NULL) {
printf("trying to add data to NULL pdu\n");
return -1;
}
if (dsize == 0) {
printf("Trying to append zero size data to pdu\n");
return -2;
}
if (iscsi_add_data(&pdu->outdata, dptr, dsize, 1) != 0) {
printf("failed to add data to pdu buffer\n");
return -3;
}
/* update data segment length */
*(uint32_t *)&pdu->outdata.data[4] = htonl(pdu->outdata.size-ISCSI_HEADER_SIZE);
return 0;
}
int iscsi_get_pdu_size(const unsigned char *hdr)
{
int size;
size = (ntohl(*(uint32_t *)&hdr[4])&0x00ffffff) + ISCSI_HEADER_SIZE;
size = (size+3)&0xfffffffc;
return size;
}
int iscsi_process_pdu(struct iscsi_context *iscsi, const unsigned char *hdr, int size)
{
uint32_t itt;
enum iscsi_opcode opcode;
struct iscsi_pdu *pdu;
uint8_t ahslen;
opcode = hdr[0] & 0x3f;
ahslen = hdr[4];
itt = ntohl(*(uint32_t *)&hdr[16]);
if (ahslen != 0) {
printf("cant handle expanded headers yet\n");
return -1;
}
for (pdu = iscsi->waitpdu; pdu; pdu = pdu->next) {
enum iscsi_opcode expected_response = pdu->response_opcode;
int is_finished = 1;
if (pdu->itt != itt) {
continue;
}
/* we have a special case with scsi-command opcodes, the are replied to by either a scsi-response
* or a data-in, or a combination of both.
*/
if (opcode == ISCSI_PDU_DATA_IN && expected_response == ISCSI_PDU_SCSI_RESPONSE) {
expected_response = ISCSI_PDU_DATA_IN;
}
if (opcode != expected_response) {
printf("Got wrong opcode back for itt:%d got:%d expected %d\n", itt, opcode, pdu->response_opcode);
return -1;
}
switch (opcode) {
case ISCSI_PDU_LOGIN_RESPONSE:
if (iscsi_process_login_reply(iscsi, pdu, hdr, size) != 0) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
printf("iscsi login reply failed\n");
return -2;
}
break;
case ISCSI_PDU_TEXT_RESPONSE:
if (iscsi_process_text_reply(iscsi, pdu, hdr, size) != 0) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
printf("iscsi text reply failed\n");
return -2;
}
break;
case ISCSI_PDU_LOGOUT_RESPONSE:
if (iscsi_process_logout_reply(iscsi, pdu, hdr, size) != 0) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
printf("iscsi logout reply failed\n");
return -3;
}
break;
case ISCSI_PDU_SCSI_RESPONSE:
if (iscsi_process_scsi_reply(iscsi, pdu, hdr, size) != 0) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
printf("iscsi response reply failed\n");
return -4;
}
break;
case ISCSI_PDU_DATA_IN:
if (iscsi_process_scsi_data_in(iscsi, pdu, hdr, size, &is_finished) != 0) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
printf("iscsi data in failed\n");
return -4;
}
break;
case ISCSI_PDU_NOP_IN:
if (iscsi_process_nop_out_reply(iscsi, pdu, hdr, size) != 0) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
printf("iscsi nop-in failed\n");
return -5;
}
break;
default:
printf("Dont know how to handle opcode %d\n", opcode);
return -2;
}
if (is_finished) {
DLIST_REMOVE(iscsi->waitpdu, pdu);
iscsi_free_pdu(iscsi, pdu);
} else {
printf("pdu is not yet finished, let it remain\n");
}
return 0;
}
return 0;
}
void iscsi_pdu_set_pduflags(struct iscsi_pdu *pdu, unsigned char flags)
{
pdu->outdata.data[1] = flags;
}
void iscsi_pdu_set_immediate(struct iscsi_pdu *pdu)
{
pdu->outdata.data[0] |= ISCSI_PDU_IMMEDIATE;
}
void iscsi_pdu_set_ttt(struct iscsi_pdu *pdu, uint32_t ttt)
{
*(uint32_t *)&pdu->outdata.data[20] = htonl(ttt);
}
void iscsi_pdu_set_cmdsn(struct iscsi_pdu *pdu, uint32_t cmdsn)
{
*(uint32_t *)&pdu->outdata.data[24] = htonl(cmdsn);
}
void iscsi_pdu_set_expstatsn(struct iscsi_pdu *pdu, uint32_t expstatsnsn)
{
*(uint32_t *)&pdu->outdata.data[28] = htonl(expstatsnsn);
}
void iscsi_pdu_set_cdb(struct iscsi_pdu *pdu, struct scsi_task *task)
{
bzero(&pdu->outdata.data[32], 16);
memcpy(&pdu->outdata.data[32], task->cdb, task->cdb_size);
}
void iscsi_pdu_set_lun(struct iscsi_pdu *pdu, uint32_t lun)
{
pdu->outdata.data[9] = lun;
}
void iscsi_pdu_set_expxferlen(struct iscsi_pdu *pdu, uint32_t expxferlen)
{
*(uint32_t *)&pdu->outdata.data[20] = htonl(expxferlen);
}
This diff is collapsed.
This diff is collapsed.
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef CCAN_ISCSI_SCSI_LOWLEVEL_H
#define CCAN_ISCSI_SCSI_LOWLEVEL_H
#define SCSI_CDB_MAX_SIZE 16
enum scsi_opcode {SCSI_OPCODE_TESTUNITREADY=0x00,
SCSI_OPCODE_INQUIRY=0x12,
SCSI_OPCODE_MODESENSE6=0x1a,
SCSI_OPCODE_READCAPACITY10=0x25,
SCSI_OPCODE_READ10=0x28,
SCSI_OPCODE_WRITE10=0x2A,
SCSI_OPCODE_REPORTLUNS=0xA0};
/* sense keys */
#define SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05
#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x06
/* ascq */
#define SCSI_SENSE_ASCQ_INVALID_FIELD_IN_CDB 0x2400
#define SCSI_SENSE_ASCQ_BUS_RESET 0x2900
enum scsi_xfer_dir {SCSI_XFER_NONE=0,
SCSI_XFER_READ=1,
SCSI_XFER_WRITE=2};
struct scsi_reportluns_params {
int report_type;
};
struct scsi_readcapacity10_params {
int lba;
int pmi;
};
struct scsi_inquiry_params {
int evpd;
int page_code;
};
struct scsi_modesense6_params {
int dbd;
int pc;
int page_code;
int sub_page_code;
};
struct scsi_sense {
unsigned char error_type;
unsigned char key;
int ascq;
};
struct scsi_data {
int size;
unsigned char *data;
};
struct scsi_allocated_memory {
struct scsi_allocated_memory *prev, *next;
void *ptr;
};
struct scsi_task {
int cdb_size;
int xfer_dir;
int expxferlen;
unsigned char cdb[SCSI_CDB_MAX_SIZE];
union {
struct scsi_readcapacity10_params readcapacity10;
struct scsi_reportluns_params reportluns;
struct scsi_inquiry_params inquiry;
struct scsi_modesense6_params modesense6;
} params;
struct scsi_sense sense;
struct scsi_data datain;
struct scsi_allocated_memory *mem;
};
void scsi_free_scsi_task(struct scsi_task *task);
/*
* TESTUNITREADY
*/
struct scsi_task *scsi_cdb_testunitready(void);
/*
* REPORTLUNS
*/
#define SCSI_REPORTLUNS_REPORT_ALL_LUNS 0x00
#define SCSI_REPORTLUNS_REPORT_WELL_KNOWN_ONLY 0x01
#define SCSI_REPORTLUNS_REPORT_AVAILABLE_LUNS_ONLY 0x02
struct scsi_reportluns_list {
uint32_t num;
uint16_t luns[0];
};
struct scsi_task *scsi_reportluns_cdb(int report_type, int alloc_len);
/*
* READCAPACITY10
*/
struct scsi_readcapacity10 {
uint32_t lba;
uint32_t block_size;
};
struct scsi_task *scsi_cdb_readcapacity10(int lba, int pmi);
/*
* INQUIRY
*/
enum scsi_inquiry_peripheral_qualifier {SCSI_INQUIRY_PERIPHERAL_QUALIFIER_CONNECTED=0x00,
SCSI_INQUIRY_PERIPHERAL_QUALIFIER_DISCONNECTED=0x01,
SCSI_INQUIRY_PERIPHERAL_QUALIFIER_NOT_SUPPORTED=0x03};
enum scsi_inquiry_peripheral_device_type {SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_DIRECT_ACCESS=0x00,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQUENTIAL_ACCESS=0x01,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PRINTER=0x02,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_PROCESSOR=0x03,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WRITE_ONCE=0x04,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MMC=0x05,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SCANNER=0x06,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_MEMORY=0x07,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_MEDIA_CHANGER=0x08,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_COMMUNICATIONS=0x09,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_STORAGE_ARRAY_CONTROLLER=0x0c,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_ENCLOSURE_SERVICES=0x0d,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SIMPLIFIED_DIRECT_ACCESS=0x0e,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OPTICAL_CARD_READER=0x0f,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_BRIDGE_CONTROLLER=0x10,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_OSD=0x11,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_AUTOMATION=0x12,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_SEQURITY_MANAGER=0x13,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_WELL_KNOWN_LUN=0x1e,
SCSI_INQUIRY_PERIPHERAL_DEVICE_TYPE_UNKNOWN=0x1f};
struct scsi_inquiry_standard {
enum scsi_inquiry_peripheral_qualifier periperal_qualifier;
enum scsi_inquiry_peripheral_device_type periperal_device_type;
int rmb;
int version;
int normaca;
int hisup;
int response_data_format;
char vendor_identification[8+1];
char product_identification[16+1];
char product_revision_level[4+1];
};
struct scsi_task *scsi_cdb_inquiry(int evpd, int page_code, int alloc_len);
/*
* MODESENSE6
*/
enum scsi_modesense_page_control {SCSI_MODESENSE_PC_CURRENT=0x00,
SCSI_MODESENSE_PC_CHANGEABLE=0x01,
SCSI_MODESENSE_PC_DEFAULT=0x02,
SCSI_MODESENSE_PC_SAVED=0x03};
enum scsi_modesense_page_code {SCSI_MODESENSE_PAGECODE_RETURN_ALL_PAGES=0x3f};
struct scsi_task *scsi_cdb_modesense6(int dbd, enum scsi_modesense_page_control pc, enum scsi_modesense_page_code page_code, int sub_page_code, unsigned char alloc_len);
int scsi_datain_getfullsize(struct scsi_task *task);
void *scsi_datain_unmarshall(struct scsi_task *task);
struct scsi_task *scsi_cdb_read10(int lba, int xferlen, int blocksize);
struct scsi_task *scsi_cdb_write10(int lba, int xferlen, int fua, int fuanv, int blocksize);
#endif /* CCAN_ISCSI_SCSI_LOWLEVEL_H */
/*
Copyright (C) 2010 by Ronnie Sahlberg <ronniesahlberg@gmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <netinet/in.h>
#include <poll.h>
#include <sys/ioctl.h>
#include <arpa/inet.h>
#include "iscsi.h"
#include "iscsi-private.h"
#include "dlinklist.h"
static void set_nonblocking(int fd)
{
unsigned v;
v = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, v | O_NONBLOCK);
}
int iscsi_connect_async(struct iscsi_context *iscsi, const char *target, iscsi_command_cb cb, void *private_data)
{
int tpgt = -1;
int port = 3260;
char *str;
char *addr;
union {
struct sockaddr sa;
struct sockaddr_storage ss;
struct sockaddr_in sin;
} s;
int socksize;
if (iscsi == NULL) {
printf("Trying to connect NULL context\n");
return -1;
}
if (iscsi->fd != -1) {
printf("Trying to connect but already connected\n");
return -2;
}
addr = strdup(target);
if (addr == NULL) {
printf("failed to strdup target address\n");
return -3;
}
/* check if we have a target portal group tag */
if ((str = rindex(addr, ',')) != NULL) {
tpgt = atoi(str+1);
str[0] = 0;
}
/* XXX need handling for {ipv6 addresses} */
/* for now, assume all is ipv4 */
if ((str = rindex(addr, ':')) != NULL) {
port = atoi(str+1);
str[0] = 0;
}
s.sin.sin_family = AF_INET;
s.sin.sin_port = htons(port);
if (inet_pton(AF_INET, addr, &s.sin.sin_addr) != 1) {
printf("failed to convert to ip address\n");
free(addr);
return -4;
}
free(addr);
switch (s.ss.ss_family) {
case AF_INET:
iscsi->fd = socket(AF_INET, SOCK_STREAM, 0);
socksize = sizeof(struct sockaddr_in);
break;
default:
printf("Unknown family :%d\n", s.ss.ss_family);
return -5;
}
if (iscsi->fd == -1) {
printf("Failed to open socket\n");
return -6;
}
iscsi->connect_cb = cb;
iscsi->connect_data = private_data;
set_nonblocking(iscsi->fd);
if (connect(iscsi->fd, &s.sa, socksize) != 0 && errno != EINPROGRESS) {
printf("Connect failed errno : %s (%d)\n", strerror(errno), errno);
return -7;
}
return 0;
}
int iscsi_disconnect(struct iscsi_context *iscsi)
{
if (iscsi == NULL) {
printf("Trying to disconnect NULL context\n");
return -1;
}
if (iscsi->is_loggedin != 0) {
printf("Trying to disconnect while logged in\n");
return -2;
}
if (iscsi->fd == -1) {
printf("Trying to disconnect but not connected\n");
return -3;
}
close(iscsi->fd);
iscsi->fd = -1;
iscsi->is_connected = 0;
return 0;
}
int iscsi_get_fd(struct iscsi_context *iscsi)
{
if (iscsi == NULL) {
printf("Trying to get fd for NULL context\n");
return -1;
}
return iscsi->fd;
}
int iscsi_which_events(struct iscsi_context *iscsi)
{
int events = POLLIN;
if (iscsi->is_connected == 0) {
events |= POLLOUT;
}
if (iscsi->outqueue) {
events |= POLLOUT;
}
return events;
}
static int iscsi_read_from_socket(struct iscsi_context *iscsi)
{
int available;
int size;
unsigned char *buf;
ssize_t count;
if (ioctl(iscsi->fd, FIONREAD, &available) != 0) {
printf("ioctl FIONREAD returned error : %d\n", errno);
return -1;
}
if (available == 0) {
printf("no data readable in socket, socket is closed\n");
return -2;
}
size = iscsi->insize - iscsi->inpos + available;
buf = malloc(size);
if (buf == NULL) {
printf("failed to allocate %d bytes for input buffer\n", size);
return -3;
}
if (iscsi->insize > iscsi->inpos) {
memcpy(buf, iscsi->inbuf + iscsi->inpos, iscsi->insize - iscsi->inpos);
iscsi->insize -= iscsi->inpos;
iscsi->inpos = 0;
}
count = read(iscsi->fd, buf + iscsi->insize, available);
if (count == -1) {
if (errno == EINTR) {
free(buf);
buf = NULL;
return 0;
}
printf("read from socket failed, errno:%d\n", errno);
free(buf);
buf = NULL;
return -4;
}
if (iscsi->inbuf != NULL) {
free(iscsi->inbuf);
}
iscsi->inbuf = buf;
iscsi->insize += count;
while (1) {
if (iscsi->insize - iscsi->inpos < 48) {
return 0;
}
count = iscsi_get_pdu_size(iscsi->inbuf + iscsi->inpos);
if (iscsi->insize + iscsi->inpos < count) {
return 0;
}
if (iscsi_process_pdu(iscsi, iscsi->inbuf + iscsi->inpos, count) != 0) {
printf("failed to process pdu\n");
return -5;
}
iscsi->inpos += count;
if (iscsi->inpos == iscsi->insize) {
free(iscsi->inbuf);
iscsi->inbuf = NULL;
iscsi->insize = 0;
iscsi->inpos = 0;
}
if (iscsi->inpos > iscsi->insize) {
printf("inpos > insize. bug!\n");
return -6;
}
}
return 0;
}
static int iscsi_write_to_socket(struct iscsi_context *iscsi)
{
ssize_t count;
if (iscsi == NULL) {
printf("trying to write to socket for NULL context\n");
return -1;
}
if (iscsi->fd == -1) {
printf("trying to write but not connected\n");
return -2;
}
while (iscsi->outqueue != NULL) {
ssize_t total;
total = iscsi->outqueue->outdata.size;
total = (total +3) & 0xfffffffc;
count = write(iscsi->fd, iscsi->outqueue->outdata.data + iscsi->outqueue->written, total - iscsi->outqueue->written);
if (count == -1) {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
printf("socket would block, return from write to socket\n");
return 0;
}
printf("Error when writing to socket :%d\n", errno);
return -3;
}
iscsi->outqueue->written += count;
if (iscsi->outqueue->written == total) {
struct iscsi_pdu *pdu = iscsi->outqueue;
DLIST_REMOVE(iscsi->outqueue, pdu);
DLIST_ADD_END(iscsi->waitpdu, pdu, NULL);
}
}
return 0;
}
int iscsi_service(struct iscsi_context *iscsi, int revents)
{
if (revents & POLLERR) {
printf("iscsi_service: POLLERR, socket error\n");
iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
return -1;
}
if (revents & POLLHUP) {
printf("iscsi_service: POLLHUP, socket error\n");
iscsi->connect_cb(iscsi, ISCSI_STATUS_ERROR, NULL, iscsi->connect_data);
return -2;
}
if (iscsi->is_connected == 0 && iscsi->fd != -1 && revents&POLLOUT) {
iscsi->is_connected = 1;
iscsi->connect_cb(iscsi, ISCSI_STATUS_GOOD, NULL, iscsi->connect_data);
return 0;
}
if (revents & POLLOUT && iscsi->outqueue != NULL) {
if (iscsi_write_to_socket(iscsi) != 0) {
printf("write to socket failed\n");
return -3;
}
}
if (revents & POLLIN) {
if (iscsi_read_from_socket(iscsi) != 0) {
printf("read from socket failed\n");
return -4;
}
}
return 0;
}
int iscsi_queue_pdu(struct iscsi_context *iscsi, struct iscsi_pdu *pdu)
{
if (iscsi == NULL) {
printf("trying to queue to NULL context\n");
return -1;
}
if (pdu == NULL) {
printf("trying to queue NULL pdu\n");
return -2;
}
DLIST_ADD_END(iscsi->outqueue, pdu, NULL);
return 0;
}
#include <ccan/iscsi/iscsi.h>
#include <ccan/iscsi/discovery.c>
#include <ccan/iscsi/socket.c>
#include <ccan/iscsi/init.c>
#include <ccan/iscsi/pdu.c>
#include <ccan/iscsi/scsi-lowlevel.c>
#include <ccan/iscsi/nop.c>
#include <ccan/iscsi/login.c>
#include <ccan/iscsi/scsi-command.c>
#include <ccan/tap/tap.h>
int main(void)
{
struct iscsi_context *iscsi;
plan_tests(2);
iscsi = iscsi_create_context("some name");
ok1(iscsi);
ok1(iscsi_destroy_context(iscsi) == 0);
/* This exits depending on whether all tests passed */
return exit_status();
}
This diff is collapsed.
......@@ -33,7 +33,7 @@ struct jbitset {
JError_t err;
const char *errstr;
};
const char *COLD_ATTRIBUTE jbit_error_(struct jbitset *set);
const char *COLD jbit_error_(struct jbitset *set);
/**
* jbit_error - test for an error in the a previous jbit_ operation.
......
......@@ -44,7 +44,7 @@ struct jmap {
unsigned long acc_index;
const char *funcname;
};
const char *COLD_ATTRIBUTE jmap_error_(struct jmap *map);
const char *COLD jmap_error_(struct jmap *map);
/* Debugging checks. */
static inline void jmap_debug_add_access(const struct jmap *map,
......
......@@ -39,7 +39,7 @@
* code path and optimize appropriately; see likely() above.
*
* See Also:
* likely(), likely_stats(), UNLIKELY_FUNCTION_ATTRIBUTE (compiler.h)
* likely(), likely_stats(), COLD (compiler.h)
*
* Example:
* // Prints a warning if we overflow.
......
CC=gcc
CFLAGS=-g -O0 -Wall -W -I../.. "-D_U_=__attribute__((unused))" -D_FILE_OFFSET_BITS=64
LIBS=
LIBNFS_OBJ = libnfs-raw-mount.o libnfs-raw-portmap.o libnfs-raw-nfs.o libnfs-raw-nfsacl.o mount.o nfs.o nfsacl.o portmap.o pdu.o init.o socket.o libnfs.o libnfs-sync.o
all: tools/nfsclient-raw tools/nfsclient-async tools/nfsclient-sync
tools/nfsclient-async: tools/nfsclient-async.c libnfs.a
$(CC) $(CFLAGS) -o $@ tools/nfsclient-async.c libnfs.a $(LIBS)
tools/nfsclient-sync: tools/nfsclient-sync.c libnfs.a
$(CC) $(CFLAGS) -o $@ tools/nfsclient-sync.c libnfs.a $(LIBS)
tools/nfsclient-raw: tools/nfsclient-raw.c libnfs.a
$(CC) $(CFLAGS) -o $@ tools/nfsclient-raw.c libnfs.a $(LIBS)
libnfs.a: $(LIBNFS_OBJ)
@echo Creating library $@
ar r libnfs.a $(LIBNFS_OBJ)
ranlib libnfs.a
rpc/mount.h: rpc/mount.x
@echo Generating $@
rpcgen -h rpc/mount.x > $@
libnfs-raw-mount.c: rpc/mount.x
@echo Generating $@
rpcgen -c rpc/mount.x > libnfs-raw-mount.c
libnfs-raw-mount.o: libnfs-raw-mount.c rpc/mount.h
@echo Compiling $@
gcc -g -c libnfs-raw-mount.c -o $@
rpc/nfs.h: rpc/nfs.x
@echo Generating $@
rpcgen -h rpc/nfs.x > $@
libnfs-raw-nfs.c: rpc/nfs.x
@echo Generating $@
rpcgen -c rpc/nfs.x > $@
libnfs-raw-nfs.o: libnfs-raw-nfs.c rpc/nfs.h
@echo Compiling $@
gcc -g -c libnfs-raw-nfs.c -o $@
rpc/nfsacl.h: rpc/nfsacl.x
@echo Generating $@
rpcgen -h rpc/nfsacl.x > $@
libnfs-raw-nfsacl.c: rpc/nfsacl.x
@echo Generating $@
rpcgen -c rpc/nfsacl.x > $@
libnfs-raw-nfsacl.o: libnfs-raw-nfsacl.c rpc/nfsacl.h
@echo Compiling $@
gcc -g -c libnfs-raw-nfsacl.c -o $@
rpc/portmap.h: rpc/portmap.x
@echo Generating $@
rpcgen -h rpc/portmap.x > $@
libnfs-raw-portmap.c: rpc/portmap.x
@echo Generating $@
rpcgen -c rpc/portmap.x > $@
libnfs-raw-portmap.o: libnfs-raw-portmap.c rpc/portmap.h
@echo Compiling $@
gcc -g -c libnfs-raw-portmap.c -o $@
clean:
rm -f *.o
rm -f *.a
rm -f rpc/mount.h libnfs-raw-mount.c
rm -f rpc/nfs.h libnfs-raw-nfs.c
rm -f rpc/nfsacl.h libnfs-raw-nfsacl.c
rm -f rpc/portmap.h libnfs-raw-portmap.c
rm -f tools/nfsclient-raw tools/nfsclient-async tools/nfsclient-sync
/*
Unix SMB/CIFS implementation.
some simple double linked list macros
Copyright (C) Andrew Tridgell 1998-2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* To use these macros you must have a structure containing a next and
prev pointer */
#ifndef _DLINKLIST_H
#define _DLINKLIST_H
/*
February 2010 - changed list format to have a prev pointer from the
list head. This makes DLIST_ADD_END() O(1) even though we only have
one list pointer.
The scheme is as follows:
1) with no entries in the list:
list_head == NULL
2) with 1 entry in the list:
list_head->next == NULL
list_head->prev == list_head
3) with 2 entries in the list:
list_head->next == element2
list_head->prev == element2
element2->prev == list_head
element2->next == NULL
4) with N entries in the list:
list_head->next == element2
list_head->prev == elementN
elementN->prev == element{N-1}
elementN->next == NULL
This allows us to find the tail of the list by using
list_head->prev, which means we can add to the end of the list in
O(1) time
Note that the 'type' arguments below are no longer needed, but
are kept for now to prevent an incompatible argument change
*/
/*
add an element at the front of a list
*/
#define DLIST_ADD(list, p) \
do { \
if (!(list)) { \
(p)->prev = (list) = (p); \
(p)->next = NULL; \
} else { \
(p)->prev = (list)->prev; \
(list)->prev = (p); \
(p)->next = (list); \
(list) = (p); \
} \
} while (0)
/*
remove an element from a list
Note that the element doesn't have to be in the list. If it
isn't then this is a no-op
*/
#define DLIST_REMOVE(list, p) \
do { \
if ((p) == (list)) { \
if ((p)->next) (p)->next->prev = (p)->prev; \
(list) = (p)->next; \
} else if ((list) && (p) == (list)->prev) { \
(p)->prev->next = NULL; \
(list)->prev = (p)->prev; \
} else { \
if ((p)->prev) (p)->prev->next = (p)->next; \
if ((p)->next) (p)->next->prev = (p)->prev; \
} \
if ((p) != (list)) (p)->next = (p)->prev = NULL; \
} while (0)
/*
find the head of the list given any element in it.
Note that this costs O(N), so you should avoid this macro
if at all possible!
*/
#define DLIST_HEAD(p, result_head) \
do { \
(result_head) = (p); \
while (DLIST_PREV(result_head)) (result_head) = (result_head)->prev; \
} while(0)
/* return the last element in the list */
#define DLIST_TAIL(list) ((list)?(list)->prev:NULL)
/* return the previous element in the list. */
#define DLIST_PREV(p) (((p)->prev && (p)->prev->next != NULL)?(p)->prev:NULL)
/* insert 'p' after the given element 'el' in a list. If el is NULL then
this is the same as a DLIST_ADD() */
#define DLIST_ADD_AFTER(list, p, el) \
do { \
if (!(list) || !(el)) { \
DLIST_ADD(list, p); \
} else { \
(p)->prev = (el); \
(p)->next = (el)->next; \
(el)->next = (p); \
if ((p)->next) (p)->next->prev = (p); \
if ((list)->prev == (el)) (list)->prev = (p); \
}\
} while (0)
/*
add to the end of a list.
Note that 'type' is ignored
*/
#define DLIST_ADD_END(list, p, type) \
do { \
if (!(list)) { \
DLIST_ADD(list, p); \
} else { \
DLIST_ADD_AFTER(list, p, (list)->prev); \
} \
} while (0)
/* promote an element to the from of a list */
#define DLIST_PROMOTE(list, p) \
do { \
DLIST_REMOVE(list, p); \
DLIST_ADD(list, p); \
} while (0)
/*
demote an element to the end of a list.
Note that 'type' is ignored
*/
#define DLIST_DEMOTE(list, p, type) \
do { \
DLIST_REMOVE(list, p); \
DLIST_ADD_END(list, p, NULL); \
} while (0)
/*
concatenate two lists - putting all elements of the 2nd list at the
end of the first list.
Note that 'type' is ignored
*/
#define DLIST_CONCATENATE(list1, list2, type) \
do { \
if (!(list1)) { \
(list1) = (list2); \
} else { \
(list1)->prev->next = (list2); \
if (list2) { \
void *_tmplist = (void *)(list1)->prev; \
(list1)->prev = (list2)->prev; \
(list2)->prev = _tmplist; \
} \
} \
} while (0)
#endif /* _DLINKLIST_H */
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <rpc/xdr.h>
#include "dlinklist.h"
#include "nfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
struct rpc_context *rpc_init_context(void)
{
struct rpc_context *rpc;
rpc = malloc(sizeof(struct rpc_context));
if (rpc == NULL) {
printf("Failed to allocate rpc context\n");
return NULL;
}
bzero(rpc, sizeof(struct rpc_context));
rpc->encodebuflen = 65536;
rpc->encodebuf = malloc(rpc->encodebuflen);
if (rpc->encodebuf == NULL) {
printf("Failed to allocate a buffer for rpc encoding\n");
free(rpc);
return NULL;
}
rpc->auth = authunix_create_default();
if (rpc->auth == NULL) {
printf("failed to create authunix\n");
free(rpc->encodebuf);
free(rpc);
return NULL;
}
rpc->xid = 1;
rpc->fd = -1;
return rpc;
}
void rpc_set_auth(struct rpc_context *rpc, struct AUTH *auth)
{
if (rpc->auth != NULL) {
auth_destroy(rpc->auth);
}
rpc->auth = auth;
}
void rpc_set_error(struct rpc_context *rpc, char *error_string, ...)
{
va_list ap;
char *str;
if (rpc->error_string != NULL) {
free(rpc->error_string);
}
va_start(ap, error_string);
vasprintf(&str, error_string, ap);
rpc->error_string = str;
va_end(ap);
}
char *rpc_get_error(struct rpc_context *rpc)
{
return rpc->error_string;
}
void rpc_error_all_pdus(struct rpc_context *rpc, char *error)
{
struct rpc_pdu *pdu;
while((pdu = rpc->outqueue) != NULL) {
pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data);
DLIST_REMOVE(rpc->outqueue, pdu);
rpc_free_pdu(rpc, pdu);
}
while((pdu = rpc->waitpdu) != NULL) {
pdu->cb(rpc, RPC_STATUS_ERROR, error, pdu->private_data);
DLIST_REMOVE(rpc->waitpdu, pdu);
rpc_free_pdu(rpc, pdu);
}
}
void rpc_destroy_context(struct rpc_context *rpc)
{
struct rpc_pdu *pdu;
while((pdu = rpc->outqueue) != NULL) {
pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data);
DLIST_REMOVE(rpc->outqueue, pdu);
rpc_free_pdu(rpc, pdu);
}
while((pdu = rpc->waitpdu) != NULL) {
pdu->cb(rpc, RPC_STATUS_CANCEL, NULL, pdu->private_data);
DLIST_REMOVE(rpc->waitpdu, pdu);
rpc_free_pdu(rpc, pdu);
}
auth_destroy(rpc->auth);
rpc->auth =NULL;
if (rpc->fd != -1) {
close(rpc->fd);
}
if (rpc->encodebuf != NULL) {
free(rpc->encodebuf);
rpc->encodebuf = NULL;
}
if (rpc->error_string != NULL) {
free(rpc->error_string);
rpc->error_string = NULL;
}
free(rpc);
}
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <rpc/auth.h>
struct rpc_context {
int fd;
int is_connected;
char *error_string;
rpc_cb connect_cb;
void *connect_data;
AUTH *auth;
unsigned long xid;
/* buffer used for encoding RPC PDU */
char *encodebuf;
int encodebuflen;
struct rpc_pdu *outqueue;
struct rpc_pdu *waitpdu;
int insize;
int inpos;
char *inbuf;
};
struct rpc_pdu {
struct rpc_pdu *prev, *next;
unsigned long xid;
XDR xdr;
int written;
struct rpc_data outdata;
rpc_cb cb;
void *private_data;
/* function to decode the xdr reply data and buffer to decode into */
xdrproc_t xdr_decode_fn;
caddr_t xdr_decode_buf;
int xdr_decode_bufsize;
};
struct rpc_pdu *rpc_allocate_pdu(struct rpc_context *rpc, int program, int version, int procedure, rpc_cb cb, void *private_data, xdrproc_t xdr_decode_fn, int xdr_bufsize);
void rpc_free_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu);
int rpc_queue_pdu(struct rpc_context *rpc, struct rpc_pdu *pdu);
int rpc_get_pdu_size(char *buf);
int rpc_process_pdu(struct rpc_context *rpc, char *buf, int size);
void rpc_error_all_pdus(struct rpc_context *rpc, char *error);
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "rpc/mount.h"
bool_t
xdr_fhandle3 (XDR *xdrs, fhandle3 *objp)
{
register int32_t *buf;
if (!xdr_bytes (xdrs, (char **)&objp->fhandle3_val, (u_int *) &objp->fhandle3_len, FHSIZE3))
return FALSE;
return TRUE;
}
bool_t
xdr_dirpath (XDR *xdrs, dirpath *objp)
{
register int32_t *buf;
if (!xdr_string (xdrs, objp, MNTPATHLEN))
return FALSE;
return TRUE;
}
bool_t
xdr_name (XDR *xdrs, name *objp)
{
register int32_t *buf;
if (!xdr_string (xdrs, objp, MNTNAMLEN))
return FALSE;
return TRUE;
}
bool_t
xdr_mountstat3 (XDR *xdrs, mountstat3 *objp)
{
register int32_t *buf;
if (!xdr_enum (xdrs, (enum_t *) objp))
return FALSE;
return TRUE;
}
bool_t
xdr_mountlist (XDR *xdrs, mountlist *objp)
{
register int32_t *buf;
if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct mountbody), (xdrproc_t) xdr_mountbody))
return FALSE;
return TRUE;
}
bool_t
xdr_mountbody (XDR *xdrs, mountbody *objp)
{
register int32_t *buf;
if (!xdr_name (xdrs, &objp->ml_hostname))
return FALSE;
if (!xdr_dirpath (xdrs, &objp->ml_directory))
return FALSE;
if (!xdr_mountlist (xdrs, &objp->ml_next))
return FALSE;
return TRUE;
}
bool_t
xdr_groups (XDR *xdrs, groups *objp)
{
register int32_t *buf;
if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct groupnode), (xdrproc_t) xdr_groupnode))
return FALSE;
return TRUE;
}
bool_t
xdr_groupnode (XDR *xdrs, groupnode *objp)
{
register int32_t *buf;
if (!xdr_name (xdrs, &objp->gr_name))
return FALSE;
if (!xdr_groups (xdrs, &objp->gr_next))
return FALSE;
return TRUE;
}
bool_t
xdr_exports (XDR *xdrs, exports *objp)
{
register int32_t *buf;
if (!xdr_pointer (xdrs, (char **)objp, sizeof (struct exportnode), (xdrproc_t) xdr_exportnode))
return FALSE;
return TRUE;
}
bool_t
xdr_exportnode (XDR *xdrs, exportnode *objp)
{
register int32_t *buf;
if (!xdr_dirpath (xdrs, &objp->ex_dir))
return FALSE;
if (!xdr_groups (xdrs, &objp->ex_groups))
return FALSE;
if (!xdr_exports (xdrs, &objp->ex_next))
return FALSE;
return TRUE;
}
bool_t
xdr_mountres3_ok (XDR *xdrs, mountres3_ok *objp)
{
register int32_t *buf;
if (!xdr_fhandle3 (xdrs, &objp->fhandle))
return FALSE;
if (!xdr_array (xdrs, (char **)&objp->auth_flavors.auth_flavors_val, (u_int *) &objp->auth_flavors.auth_flavors_len, ~0,
sizeof (int), (xdrproc_t) xdr_int))
return FALSE;
return TRUE;
}
bool_t
xdr_mountres3 (XDR *xdrs, mountres3 *objp)
{
register int32_t *buf;
if (!xdr_mountstat3 (xdrs, &objp->fhs_status))
return FALSE;
switch (objp->fhs_status) {
case MNT3_OK:
if (!xdr_mountres3_ok (xdrs, &objp->mountres3_u.mountinfo))
return FALSE;
break;
default:
break;
}
return TRUE;
}
This diff is collapsed.
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "rpc/nfsacl.h"
/*
* Please do not edit this file.
* It was generated using rpcgen.
*/
#include "rpc/portmap.h"
bool_t
xdr_mapping (XDR *xdrs, mapping *objp)
{
register int32_t *buf;
if (xdrs->x_op == XDR_ENCODE) {
buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
if (buf == NULL) {
if (!xdr_u_int (xdrs, &objp->prog))
return FALSE;
if (!xdr_u_int (xdrs, &objp->vers))
return FALSE;
if (!xdr_u_int (xdrs, &objp->prot))
return FALSE;
if (!xdr_u_int (xdrs, &objp->port))
return FALSE;
} else {
IXDR_PUT_U_LONG(buf, objp->prog);
IXDR_PUT_U_LONG(buf, objp->vers);
IXDR_PUT_U_LONG(buf, objp->prot);
IXDR_PUT_U_LONG(buf, objp->port);
}
return TRUE;
} else if (xdrs->x_op == XDR_DECODE) {
buf = XDR_INLINE (xdrs, 4 * BYTES_PER_XDR_UNIT);
if (buf == NULL) {
if (!xdr_u_int (xdrs, &objp->prog))
return FALSE;
if (!xdr_u_int (xdrs, &objp->vers))
return FALSE;
if (!xdr_u_int (xdrs, &objp->prot))
return FALSE;
if (!xdr_u_int (xdrs, &objp->port))
return FALSE;
} else {
objp->prog = IXDR_GET_U_LONG(buf);
objp->vers = IXDR_GET_U_LONG(buf);
objp->prot = IXDR_GET_U_LONG(buf);
objp->port = IXDR_GET_U_LONG(buf);
}
return TRUE;
}
if (!xdr_u_int (xdrs, &objp->prog))
return FALSE;
if (!xdr_u_int (xdrs, &objp->vers))
return FALSE;
if (!xdr_u_int (xdrs, &objp->prot))
return FALSE;
if (!xdr_u_int (xdrs, &objp->port))
return FALSE;
return TRUE;
}
bool_t
xdr_call_args (XDR *xdrs, call_args *objp)
{
register int32_t *buf;
if (xdrs->x_op == XDR_ENCODE) {
buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
if (buf == NULL) {
if (!xdr_u_int (xdrs, &objp->prog))
return FALSE;
if (!xdr_u_int (xdrs, &objp->vers))
return FALSE;
if (!xdr_u_int (xdrs, &objp->proc))
return FALSE;
} else {
IXDR_PUT_U_LONG(buf, objp->prog);
IXDR_PUT_U_LONG(buf, objp->vers);
IXDR_PUT_U_LONG(buf, objp->proc);
}
if (!xdr_bytes (xdrs, (char **)&objp->args.args_val, (u_int *) &objp->args.args_len, ~0))
return FALSE;
return TRUE;
} else if (xdrs->x_op == XDR_DECODE) {
buf = XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
if (buf == NULL) {
if (!xdr_u_int (xdrs, &objp->prog))
return FALSE;
if (!xdr_u_int (xdrs, &objp->vers))
return FALSE;
if (!xdr_u_int (xdrs, &objp->proc))
return FALSE;
} else {
objp->prog = IXDR_GET_U_LONG(buf);
objp->vers = IXDR_GET_U_LONG(buf);
objp->proc = IXDR_GET_U_LONG(buf);
}
if (!xdr_bytes (xdrs, (char **)&objp->args.args_val, (u_int *) &objp->args.args_len, ~0))
return FALSE;
return TRUE;
}
if (!xdr_u_int (xdrs, &objp->prog))
return FALSE;
if (!xdr_u_int (xdrs, &objp->vers))
return FALSE;
if (!xdr_u_int (xdrs, &objp->proc))
return FALSE;
if (!xdr_bytes (xdrs, (char **)&objp->args.args_val, (u_int *) &objp->args.args_len, ~0))
return FALSE;
return TRUE;
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#include <rpc/xdr.h>
#include "nfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "rpc/mount.h"
int rpc_mount_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/null call");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/null call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_mnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_MNT, cb, private_data, (xdrproc_t)xdr_mountres3, sizeof(mountres3));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for mount/mnt call");
return -1;
}
if (xdr_dirpath(&pdu->xdr, &export) == 0) {
rpc_set_error(rpc, "XDR error. Failed to encode mount/mnt call");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for mount/mnt call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_dump_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_DUMP, cb, private_data, (xdrproc_t)xdr_mountlist, sizeof(mountlist));
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/dump\n");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/dump pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_umnt_async(struct rpc_context *rpc, rpc_cb cb, char *export, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNT, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/umnt\n");
return -1;
}
if (xdr_dirpath(&pdu->xdr, &export) == 0) {
printf("failed to encode dirpath for mount/umnt\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/umnt pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_umntall_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_UMNTALL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/umntall\n");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/umntall pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_mount_export_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, MOUNT_PROGRAM, MOUNT_V3, MOUNT3_EXPORT, cb, private_data, (xdrproc_t)xdr_exports, sizeof(exports));
if (pdu == NULL) {
printf("Failed to allocate pdu for mount/export\n");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue mount/export pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
char *mountstat3_to_str(int st)
{
enum mountstat3 stat = st;
char *str = "unknown mount stat";
switch (stat) {
case MNT3_OK: str="MNT3_OK"; break;
case MNT3ERR_PERM: str="MNT3ERR_PERM"; break;
case MNT3ERR_NOENT: str="MNT3ERR_NOENT"; break;
case MNT3ERR_IO: str="MNT3ERR_IO"; break;
case MNT3ERR_ACCES: str="MNT3ERR_ACCES"; break;
case MNT3ERR_NOTDIR: str="MNT3ERR_NOTDIR"; break;
case MNT3ERR_INVAL: str="MNT3ERR_INVAL"; break;
case MNT3ERR_NAMETOOLONG: str="MNT3ERR_NAMETOOLONG"; break;
case MNT3ERR_NOTSUPP: str="MNT3ERR_NOTSUPP"; break;
case MNT3ERR_SERVERFAULT: str="MNT3ERR_SERVERFAULT"; break;
}
return str;
}
int mountstat3_to_errno(int st)
{
enum mountstat3 stat = st;
switch (stat) {
case MNT3_OK: return 0; break;
case MNT3ERR_PERM: return -EPERM; break;
case MNT3ERR_NOENT: return -EPERM; break;
case MNT3ERR_IO: return -EIO; break;
case MNT3ERR_ACCES: return -EACCES; break;
case MNT3ERR_NOTDIR: return -ENOTDIR; break;
case MNT3ERR_INVAL: return -EINVAL; break;
case MNT3ERR_NAMETOOLONG: return -E2BIG; break;
case MNT3ERR_NOTSUPP: return -EINVAL; break;
case MNT3ERR_SERVERFAULT: return -EIO; break;
}
return -ERANGE;
}
This diff is collapsed.
This diff is collapsed.
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <errno.h>
#include <rpc/xdr.h>
#include "nfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "rpc/nfsacl.h"
int rpc_nfsacl_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, NFSACL_PROGRAM, NFSACL_V3, NFSACL3_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for nfsacl/null call");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for nfsacl/null call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
This diff is collapsed.
/*
Copyright (C) by Ronnie Sahlberg <ronniesahlberg@gmail.com> 2010
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <rpc/xdr.h>
#include "nfs.h"
#include "libnfs-raw.h"
#include "libnfs-private.h"
#include "rpc/portmap.h"
int rpc_pmap_null_async(struct rpc_context *rpc, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_NULL, cb, private_data, (xdrproc_t)xdr_void, 0);
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/null call");
return -1;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
rpc_set_error(rpc, "Out of memory. Failed to queue pdu for portmap/null call");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
int rpc_pmap_getport_async(struct rpc_context *rpc, int program, int version, rpc_cb cb, void *private_data)
{
struct rpc_pdu *pdu;
struct mapping m;
pdu = rpc_allocate_pdu(rpc, PMAP_PROGRAM, PMAP_V2, PMAP_GETPORT, cb, private_data, (xdrproc_t)xdr_int, sizeof(uint32_t));
if (pdu == NULL) {
rpc_set_error(rpc, "Out of memory. Failed to allocate pdu for portmap/getport call");
return -1;
}
m.prog = program;
m.vers = version;
m.prot = IPPROTO_TCP;
m.port = 0;
if (xdr_mapping(&pdu->xdr, &m) == 0) {
rpc_set_error(rpc, "XDR error: Failed to encode data for portmap/getport call");
rpc_free_pdu(rpc, pdu);
return -2;
}
if (rpc_queue_pdu(rpc, pdu) != 0) {
printf("Failed to queue portmap/getport pdu\n");
rpc_free_pdu(rpc, pdu);
return -2;
}
return 0;
}
This diff is collapsed.
/* copied from RFC1813 */
const MNTPATHLEN = 1024; /* Maximum bytes in a path name */
const MNTNAMLEN = 255; /* Maximum bytes in a name */
const FHSIZE3 = 64; /* Maximum bytes in a V3 file handle */
typedef opaque fhandle3<FHSIZE3>;
typedef string dirpath<MNTPATHLEN>;
typedef string name<MNTNAMLEN>;
enum mountstat3 {
MNT3_OK = 0, /* no error */
MNT3ERR_PERM = 1, /* Not owner */
MNT3ERR_NOENT = 2, /* No such file or directory */
MNT3ERR_IO = 5, /* I/O error */
MNT3ERR_ACCES = 13, /* Permission denied */
MNT3ERR_NOTDIR = 20, /* Not a directory */
MNT3ERR_INVAL = 22, /* Invalid argument */
MNT3ERR_NAMETOOLONG = 63, /* Filename too long */
MNT3ERR_NOTSUPP = 10004, /* Operation not supported */
MNT3ERR_SERVERFAULT = 10006 /* A failure on the server */
};
typedef struct mountbody *mountlist;
struct mountbody {
name ml_hostname;
dirpath ml_directory;
mountlist ml_next;
};
typedef struct groupnode *groups;
struct groupnode {
name gr_name;
groups gr_next;
};
typedef struct exportnode *exports;
struct exportnode {
dirpath ex_dir;
groups ex_groups;
exports ex_next;
};
struct mountres3_ok {
fhandle3 fhandle;
int auth_flavors<>;
};
union mountres3 switch (mountstat3 fhs_status) {
case MNT3_OK:
mountres3_ok mountinfo;
default:
void;
};
program MOUNT_PROGRAM {
version MOUNT_V3 {
void MOUNT3_NULL(void) = 0;
mountres3 MOUNT3_MNT(dirpath) = 1;
mountlist MOUNT3_DUMP(void) = 2;
void MOUNT3_UMNT(dirpath) = 3;
void MOUNT3_UMNTALL(void) = 4;
exports MOUNT3_EXPORT(void) = 5;
} = 3;
} = 100005;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/* deducted from wireshark traces */
program NFSACL_PROGRAM {
version NFSACL_V3 {
void NFSACL3_NULL(void) = 0;
} = 3;
} = 100227;
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
#include <ccan/rbtree/rbtree.c>
#include <ccan/tap/tap.h>
int main(void)
{
/* This is how many tests you plan to run */
plan_tests(3);
/* Simple thing we expect to succeed */
ok1(some_test())
/* Same, with an explicit description of the test. */
ok(some_test(), "%s with no args should return 1", "some_test")
/* How to print out messages for debugging. */
diag("Address of some_test is %p", &some_test)
/* Conditional tests must be explicitly skipped. */
#if HAVE_SOME_FEATURE
ok1(test_some_feature())
#else
skip(1, "Don't have SOME_FEATURE")
#endif
/* This exits depending on whether all tests passed */
return exit_status();
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment