istgt-20111008/000755 000000 000000 00000000000 11643630133 013201 5ustar00rootwheel000000 000000 istgt-20111008/configure.in000644 000000 000000 00000012732 11640373417 015525 0ustar00rootwheel000000 000000 # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. AC_PREREQ(2.62) AC_INIT(istgt, 0.4) AC_CONFIG_SRCDIR([src/istgt.c]) AC_CONFIG_HEADERS([src/config.h]) # Checks for programs. AC_PROG_CC AC_PROG_INSTALL AC_PROG_RANLIB AC_PROG_SED # Checks for libraries. AC_CHECK_LIB([pthread], [pthread_create]) AC_CHECK_LIB([crypto], [MD5_Update]) AC_CHECK_LIB([cam], [cam_open_spec_device]) # Checks for header files. AC_CHECK_HEADERS([arpa/inet.h fcntl.h inttypes.h limits.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h]) # Checks for typedefs, structures, and compiler characteristics. AC_C_INLINE AC_TYPE_INT32_T AC_TYPE_INT64_T AC_TYPE_OFF_T AC_TYPE_PID_T AC_TYPE_SIZE_T AC_TYPE_SSIZE_T AC_STRUCT_ST_BLOCKS AC_TYPE_UINT16_T AC_TYPE_UINT32_T AC_TYPE_UINT64_T AC_TYPE_UINT8_T # Checks for library functions. AC_FUNC_LSTAT_FOLLOWS_SLASHED_SYMLINK AC_FUNC_MALLOC AC_FUNC_REALLOC AC_CHECK_FUNCS([ftruncate memset realpath socket strcasecmp strchr strncasecmp strspn strtol strtoull]) # check compatibility AC_SYS_LARGEFILE AC_CHECK_HEADERS([aio.h sched.h uuid.h sys/disk.h sys/disklabel.h]) AC_CHECK_HEADERS([pthread.h]) AC_CHECK_HEADERS([pthread_np.h], [], [], [#if HAVE_PTHREAD_H #include #endif ]) AC_CHECK_FUNCS([strlcpy arc4random srandomdev pthread_yield sched_yield]) AC_CHECK_FUNCS([pthread_set_name_np setproctitle]) AC_SUBST(MKDEP) AC_PATH_PROG(MKDEP, "mkdep") #if test -z "$MKDEP"; then # AC_PATH_PROG(MKDEP, "makedepend") #fi # paths configdir="$ac_default_prefix/etc/istgt" mediadir="/var/istgt" if test "$prefix" != "NONE"; then configdir="$prefix/etc/istgt" fi AC_ARG_WITH(configdir, [ --with-configdir=DIR (default $ac_default_prefix/etc/istgt)], [case $withval in yes|no) ;; *) configdir="$withval" ;; esac]) AC_SUBST(configdir) AC_ARG_WITH(mediadir, [ --with-mediadir=DIR (default /var/istgt)], [case $withval in yes|no) ;; *) mediadir="$withval" ;; esac]) AC_SUBST(mediadir) # test for -Wall saved_CFLAGS="$CFLAGS" AC_MSG_CHECKING([whether the C compiler accepts -Wall]) CFLAGS="$CFLAGS -Wall" AC_TRY_COMPILE(,, AC_MSG_RESULT(yes); saved_CFLAGS="$CFLAGS", AC_MSG_RESULT(no)) CFLAGS="$saved_CFLAGS" # check enable features AC_MSG_CHECKING([whether to use debug mode]) AC_ARG_ENABLE(debug, [ --enable-debug enable debug(logging) ], AC_MSG_RESULT(yes); AC_DEFINE(DEBUG, 1, [Define if enable debug]), AC_MSG_RESULT(no)) AC_SUBST(DEBUG) AC_MSG_CHECKING([which syslog facility to use]) AC_ARG_WITH(logfacility, [ --with-logfacility syslog facility to log with (default "local7")], [case $with_logfacility in daemon|auth|authpriv|local0|local1|local2|local3|local4|local5|local6|local7) logfacility=$with_logfacility ;; *) AC_MSG_ERROR(["unsupported facility $with_logfacility"]) ;; esac], [logfacility="local7"]) AC_DEFINE_UNQUOTED(DEFAULT_LOG_FACILITY, "$logfacility", [syslog facility]) AC_MSG_RESULT("$logfacility") AC_MSG_CHECKING([which syslog priority to use]) AC_ARG_WITH(logpriority, [ --with-logpriority syslog priority to log with (default "info")], [case $with_logpriority in emerg|alert|crit|err|warning|notice|info|debug) logpriority=$with_logpriority ;; *) AC_MSG_ERROR(["unsupported priority $with_logpriority"]) ;; esac], [logpriority="info"]) AC_DEFINE_UNQUOTED(DEFAULT_LOG_PRIORITY, "$logpriority", [syslog priority]) AC_MSG_RESULT("$logpriority") #AC_MSG_CHECKING([whether to use asynchronous io]) #AC_ARG_WITH(aio, # [ --with-aio use aio (default "no")], # [case $with_aio in # yes) # AC_DEFINE(USE_AIO, 1, [use aio]) # AC_MSG_RESULT(yes) # ;; # *) # AC_MSG_RESULT(no) # ;; # esac], # AC_MSG_RESULT(no)) #AC_SUBST(USE_AIO) AC_CHECK_HEADERS([sys/types.h]) AC_CHECK_HEADERS([machine/atomic.h], [], [], [#if HAVE_SYS_TYPES_H #include #endif ]) AC_CHECK_HEADERS([sys/atomic.h]) # for FeeeBSD #AC_CHECK_FUNCS([atomic_store_rel_int atomic_load_acq_int]) #AC_CHECK_FUNCS([atomic_cmpset_ptr atomic_cmpset_64 atomic_cmpset_32]) # for NetBSD #AC_CHECK_FUNCS([atomic_swap_uint atomic_or_uint_nv]) #AC_CHECK_FUNCS([atomic_cas_ptr atomic_cas_64 atomic_cas_32]) atomic="no" AC_MSG_CHECKING([for atomic operations]) AC_TRY_LINK([#if HAVE_SYS_TYPES_H #include #endif #include ], [ int x, y; atomic_store_rel_int(&x, 1); y = atomic_load_acq_int(&x); ], [AC_DEFINE(USE_ATOMIC, [1], [Use atomic]) AC_DEFINE(HAVE_ATOMIC_STORE_REL_INT, [1], [Define 1 if you have the function.]) AC_DEFINE(HAVE_ATOMIC_LOAD_ACQ_INT, [1], [Define 1 if you have the function.]) atomic="yes"] ) if test $atomic = "no"; then AC_TRY_LINK([ #if HAVE_SYS_TYPES_H #include #endif #include ], [ unsigned int x, y; atomic_swap_uint(&x, 1); y = atomic_or_uint_nv(&x, 0); ], [AC_DEFINE(USE_ATOMIC, [1], [Use atomic]) AC_DEFINE(HAVE_ATOMIC_SWAP_UINT, [1], [Define 1 if you have the function.]) AC_DEFINE(HAVE_ATOMIC_OR_UINT_NV, [1], [Define 1 if you have the function.]) atomic="yes"] ) fi AC_TRY_LINK([ #if HAVE_SYS_TYPES_H #include #endif #include ], [ membar_producer(); ], [AC_DEFINE(HAVE_MEMBAR_PRODUCER, [1], [Define 1 if you have the function.])] ) AC_MSG_RESULT($atomic) AC_CONFIG_FILES([Makefile src/Makefile etc/Makefile doc/Makefile]) AC_CONFIG_FILES([src/build.h]) AC_OUTPUT istgt-20111008/INSTALL000644 000000 000000 00000001012 11312643107 014222 0ustar00rootwheel000000 000000 1) run configure script # ./configure 2) build & install binary and sample configuration files # make # make install # make install-doc 3) create your configuration files and edit it for more detail, see /usr/local/share/doc/istgt/QUICKSTART # cd /usr/local/etc/istgt # cp auth.conf.sample auth.conf # cp istgt.conf.sample istgt.conf # cp istgtcontrol.conf.sample istgtcontrol.conf # EDIT files 4) start daemon # /usr/local/etc/rc.d/istgt forcestart 5) edit /etc/rc.conf to run it at startup istgt_enable="YES" istgt-20111008/configure000755 000000 000000 00000532342 11640373417 015127 0ustar00rootwheel000000 000000 #! /bin/sh # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.68 for istgt 0.4. # # # Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, # 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010 Free Software # Foundation, Inc. # # # This configure script is free software; the Free Software Foundation # gives unlimited permission to copy, distribute and modify it. ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH if test "x$CONFIG_SHELL" = x; then as_bourne_compatible="if test -n \"\${ZSH_VERSION+set}\" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which # is contrary to our usage. Disable this feature. alias -g '\${1+\"\$@\"}'='\"\$@\"' setopt NO_GLOB_SUBST else case \`(set -o) 2>/dev/null\` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi " as_required="as_fn_return () { (exit \$1); } as_fn_success () { as_fn_return 0; } as_fn_failure () { as_fn_return 1; } as_fn_ret_success () { return 0; } as_fn_ret_failure () { return 1; } exitcode=0 as_fn_success || { exitcode=1; echo as_fn_success failed.; } as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } if ( set x; as_fn_ret_success y && test x = \"\$1\" ); then : else exitcode=1; echo positional parameters were not saved. fi test x\$exitcode = x0 || exit 1" as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1 test \$(( 1 + 1 )) = 2 || exit 1" if (eval "$as_required") 2>/dev/null; then : as_have_required=yes else as_have_required=no fi if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null; then : else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR as_found=false for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. as_found=: case $as_dir in #( /*) for as_base in sh bash ksh sh5; do # Try only shells that exist, to save several forks. as_shell=$as_dir/$as_base if { test -f "$as_shell" || test -f "$as_shell.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$as_shell"; } 2>/dev/null; then : CONFIG_SHELL=$as_shell as_have_required=yes if { $as_echo "$as_bourne_compatible""$as_suggested" | as_run=a "$as_shell"; } 2>/dev/null; then : break 2 fi fi done;; esac as_found=false done $as_found || { if { test -f "$SHELL" || test -f "$SHELL.exe"; } && { $as_echo "$as_bourne_compatible""$as_required" | as_run=a "$SHELL"; } 2>/dev/null; then : CONFIG_SHELL=$SHELL as_have_required=yes fi; } IFS=$as_save_IFS if test "x$CONFIG_SHELL" != x; then : # We cannot yet assume a decent shell, so we have to provide a # neutralization value for shells without unset; and this also # works around shells that cannot unset nonexistent variables. # Preserve -v and -x to the replacement shell. BASH_ENV=/dev/null ENV=/dev/null (unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV export CONFIG_SHELL case $- in # (((( *v*x* | *x*v* ) as_opts=-vx ;; *v* ) as_opts=-v ;; *x* ) as_opts=-x ;; * ) as_opts= ;; esac exec "$CONFIG_SHELL" $as_opts "$as_myself" ${1+"$@"} fi if test x$as_have_required = xno; then : $as_echo "$0: This script requires a shell more modern than all" $as_echo "$0: the shells that I found on your system." if test x${ZSH_VERSION+set} = xset ; then $as_echo "$0: In particular, zsh $ZSH_VERSION has bugs and should" $as_echo "$0: be upgraded to zsh 4.3.4 or later." else $as_echo "$0: Please tell bug-autoconf@gnu.org about your system, $0: including any error possibly output before this $0: message. Then install a modern shell, or manually run $0: the script under such a shell if you do have one." fi exit 1 fi fi fi SHELL=${CONFIG_SHELL-/bin/sh} export SHELL # Unset more variables known to interfere with behavior of common tools. CLICOLOR_FORCE= GREP_OPTIONS= unset CLICOLOR_FORCE GREP_OPTIONS ## --------------------- ## ## M4sh Shell Functions. ## ## --------------------- ## # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits as_lineno_1=$LINENO as_lineno_1a=$LINENO as_lineno_2=$LINENO as_lineno_2a=$LINENO eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) sed -n ' p /[$]LINENO/= ' <$as_myself | sed ' s/[$]LINENO.*/&-/ t lineno b :lineno N :loop s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ t loop s/-\n.*// ' >$as_me.lineno && chmod +x "$as_me.lineno" || { $as_echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } # Don't try to exec as it changes $[0], causing all sort of problems # (the dirname of $[0] is not the place where we might find the # original and so on. Autoconf is especially sensitive to this). . "./$as_me.lineno" # Exit status is that of the last command. exit } ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" test -n "$DJDIR" || exec 7<&0 &1 # Name of the host. # hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, # so uname gets run too. ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` # # Initializations. # ac_default_prefix=/usr/local ac_clean_files= ac_config_libobj_dir=. LIBOBJS= cross_compiling=no subdirs= MFLAGS= MAKEFLAGS= # Identity of this package. PACKAGE_NAME='istgt' PACKAGE_TARNAME='istgt' PACKAGE_VERSION='0.4' PACKAGE_STRING='istgt 0.4' PACKAGE_BUGREPORT='' PACKAGE_URL='' ac_unique_file="src/istgt.c" # Factoring default headers for most tests. ac_includes_default="\ #include #ifdef HAVE_SYS_TYPES_H # include #endif #ifdef HAVE_SYS_STAT_H # include #endif #ifdef STDC_HEADERS # include # include #else # ifdef HAVE_STDLIB_H # include # endif #endif #ifdef HAVE_STRING_H # if !defined STDC_HEADERS && defined HAVE_MEMORY_H # include # endif # include #endif #ifdef HAVE_STRINGS_H # include #endif #ifdef HAVE_INTTYPES_H # include #endif #ifdef HAVE_STDINT_H # include #endif #ifdef HAVE_UNISTD_H # include #endif" ac_subst_vars='LTLIBOBJS DEBUG mediadir configdir MKDEP LIBOBJS EGREP GREP CPP SED RANLIB INSTALL_DATA INSTALL_SCRIPT INSTALL_PROGRAM OBJEXT EXEEXT ac_ct_CC CPPFLAGS LDFLAGS CFLAGS CC target_alias host_alias build_alias LIBS ECHO_T ECHO_N ECHO_C DEFS mandir localedir libdir psdir pdfdir dvidir htmldir infodir docdir oldincludedir includedir localstatedir sharedstatedir sysconfdir datadir datarootdir libexecdir sbindir bindir program_transform_name prefix exec_prefix PACKAGE_URL PACKAGE_BUGREPORT PACKAGE_STRING PACKAGE_VERSION PACKAGE_TARNAME PACKAGE_NAME PATH_SEPARATOR SHELL' ac_subst_files='' ac_user_opts=' enable_option_checking enable_largefile with_configdir with_mediadir enable_debug with_logfacility with_logpriority ' ac_precious_vars='build_alias host_alias target_alias CC CFLAGS LDFLAGS LIBS CPPFLAGS CPP' # Initialize some variables set by options. ac_init_help= ac_init_version=false ac_unrecognized_opts= ac_unrecognized_sep= # The variables have the same names as the options, with # dashes changed to underlines. cache_file=/dev/null exec_prefix=NONE no_create= no_recursion= prefix=NONE program_prefix=NONE program_suffix=NONE program_transform_name=s,x,x, silent= site= srcdir= verbose= x_includes=NONE x_libraries=NONE # Installation directory options. # These are left unexpanded so users can "make install exec_prefix=/foo" # and all the variables that are supposed to be based on exec_prefix # by default will actually change. # Use braces instead of parens because sh, perl, etc. also accept them. # (The list follows the same order as the GNU Coding Standards.) bindir='${exec_prefix}/bin' sbindir='${exec_prefix}/sbin' libexecdir='${exec_prefix}/libexec' datarootdir='${prefix}/share' datadir='${datarootdir}' sysconfdir='${prefix}/etc' sharedstatedir='${prefix}/com' localstatedir='${prefix}/var' includedir='${prefix}/include' oldincludedir='/usr/include' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' infodir='${datarootdir}/info' htmldir='${docdir}' dvidir='${docdir}' pdfdir='${docdir}' psdir='${docdir}' libdir='${exec_prefix}/lib' localedir='${datarootdir}/locale' mandir='${datarootdir}/man' ac_prev= ac_dashdash= for ac_option do # If the previous option needs an argument, assign it. if test -n "$ac_prev"; then eval $ac_prev=\$ac_option ac_prev= continue fi case $ac_option in *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; *=) ac_optarg= ;; *) ac_optarg=yes ;; esac # Accept the important Cygnus configure options, so we can diagnose typos. case $ac_dashdash$ac_option in --) ac_dashdash=yes ;; -bindir | --bindir | --bindi | --bind | --bin | --bi) ac_prev=bindir ;; -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) bindir=$ac_optarg ;; -build | --build | --buil | --bui | --bu) ac_prev=build_alias ;; -build=* | --build=* | --buil=* | --bui=* | --bu=*) build_alias=$ac_optarg ;; -cache-file | --cache-file | --cache-fil | --cache-fi \ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) ac_prev=cache_file ;; -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) cache_file=$ac_optarg ;; --config-cache | -C) cache_file=config.cache ;; -datadir | --datadir | --datadi | --datad) ac_prev=datadir ;; -datadir=* | --datadir=* | --datadi=* | --datad=*) datadir=$ac_optarg ;; -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ | --dataroo | --dataro | --datar) ac_prev=datarootdir ;; -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) datarootdir=$ac_optarg ;; -disable-* | --disable-*) ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=no ;; -docdir | --docdir | --docdi | --doc | --do) ac_prev=docdir ;; -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) docdir=$ac_optarg ;; -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) ac_prev=dvidir ;; -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) dvidir=$ac_optarg ;; -enable-* | --enable-*) ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid feature name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "enable_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval enable_$ac_useropt=\$ac_optarg ;; -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ | --exec | --exe | --ex) ac_prev=exec_prefix ;; -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ | --exec=* | --exe=* | --ex=*) exec_prefix=$ac_optarg ;; -gas | --gas | --ga | --g) # Obsolete; use --with-gas. with_gas=yes ;; -help | --help | --hel | --he | -h) ac_init_help=long ;; -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) ac_init_help=recursive ;; -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) ac_init_help=short ;; -host | --host | --hos | --ho) ac_prev=host_alias ;; -host=* | --host=* | --hos=* | --ho=*) host_alias=$ac_optarg ;; -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) ac_prev=htmldir ;; -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ | --ht=*) htmldir=$ac_optarg ;; -includedir | --includedir | --includedi | --included | --include \ | --includ | --inclu | --incl | --inc) ac_prev=includedir ;; -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ | --includ=* | --inclu=* | --incl=* | --inc=*) includedir=$ac_optarg ;; -infodir | --infodir | --infodi | --infod | --info | --inf) ac_prev=infodir ;; -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) infodir=$ac_optarg ;; -libdir | --libdir | --libdi | --libd) ac_prev=libdir ;; -libdir=* | --libdir=* | --libdi=* | --libd=*) libdir=$ac_optarg ;; -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ | --libexe | --libex | --libe) ac_prev=libexecdir ;; -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ | --libexe=* | --libex=* | --libe=*) libexecdir=$ac_optarg ;; -localedir | --localedir | --localedi | --localed | --locale) ac_prev=localedir ;; -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) localedir=$ac_optarg ;; -localstatedir | --localstatedir | --localstatedi | --localstated \ | --localstate | --localstat | --localsta | --localst | --locals) ac_prev=localstatedir ;; -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) localstatedir=$ac_optarg ;; -mandir | --mandir | --mandi | --mand | --man | --ma | --m) ac_prev=mandir ;; -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) mandir=$ac_optarg ;; -nfp | --nfp | --nf) # Obsolete; use --without-fp. with_fp=no ;; -no-create | --no-create | --no-creat | --no-crea | --no-cre \ | --no-cr | --no-c | -n) no_create=yes ;; -no-recursion | --no-recursion | --no-recursio | --no-recursi \ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) no_recursion=yes ;; -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ | --oldin | --oldi | --old | --ol | --o) ac_prev=oldincludedir ;; -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) oldincludedir=$ac_optarg ;; -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) ac_prev=prefix ;; -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) prefix=$ac_optarg ;; -program-prefix | --program-prefix | --program-prefi | --program-pref \ | --program-pre | --program-pr | --program-p) ac_prev=program_prefix ;; -program-prefix=* | --program-prefix=* | --program-prefi=* \ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) program_prefix=$ac_optarg ;; -program-suffix | --program-suffix | --program-suffi | --program-suff \ | --program-suf | --program-su | --program-s) ac_prev=program_suffix ;; -program-suffix=* | --program-suffix=* | --program-suffi=* \ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) program_suffix=$ac_optarg ;; -program-transform-name | --program-transform-name \ | --program-transform-nam | --program-transform-na \ | --program-transform-n | --program-transform- \ | --program-transform | --program-transfor \ | --program-transfo | --program-transf \ | --program-trans | --program-tran \ | --progr-tra | --program-tr | --program-t) ac_prev=program_transform_name ;; -program-transform-name=* | --program-transform-name=* \ | --program-transform-nam=* | --program-transform-na=* \ | --program-transform-n=* | --program-transform-=* \ | --program-transform=* | --program-transfor=* \ | --program-transfo=* | --program-transf=* \ | --program-trans=* | --program-tran=* \ | --progr-tra=* | --program-tr=* | --program-t=*) program_transform_name=$ac_optarg ;; -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) ac_prev=pdfdir ;; -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) pdfdir=$ac_optarg ;; -psdir | --psdir | --psdi | --psd | --ps) ac_prev=psdir ;; -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) psdir=$ac_optarg ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) silent=yes ;; -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) ac_prev=sbindir ;; -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ | --sbi=* | --sb=*) sbindir=$ac_optarg ;; -sharedstatedir | --sharedstatedir | --sharedstatedi \ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ | --sharedst | --shareds | --shared | --share | --shar \ | --sha | --sh) ac_prev=sharedstatedir ;; -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ | --sha=* | --sh=*) sharedstatedir=$ac_optarg ;; -site | --site | --sit) ac_prev=site ;; -site=* | --site=* | --sit=*) site=$ac_optarg ;; -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) ac_prev=srcdir ;; -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) srcdir=$ac_optarg ;; -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ | --syscon | --sysco | --sysc | --sys | --sy) ac_prev=sysconfdir ;; -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) sysconfdir=$ac_optarg ;; -target | --target | --targe | --targ | --tar | --ta | --t) ac_prev=target_alias ;; -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) target_alias=$ac_optarg ;; -v | -verbose | --verbose | --verbos | --verbo | --verb) verbose=yes ;; -version | --version | --versio | --versi | --vers | -V) ac_init_version=: ;; -with-* | --with-*) ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=\$ac_optarg ;; -without-* | --without-*) ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` # Reject names that are not valid shell variable names. expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && as_fn_error $? "invalid package name: $ac_useropt" ac_useropt_orig=$ac_useropt ac_useropt=`$as_echo "$ac_useropt" | sed 's/[-+.]/_/g'` case $ac_user_opts in *" "with_$ac_useropt" "*) ;; *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" ac_unrecognized_sep=', ';; esac eval with_$ac_useropt=no ;; --x) # Obsolete; use --with-x. with_x=yes ;; -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ | --x-incl | --x-inc | --x-in | --x-i) ac_prev=x_includes ;; -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) x_includes=$ac_optarg ;; -x-libraries | --x-libraries | --x-librarie | --x-librari \ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) ac_prev=x_libraries ;; -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) x_libraries=$ac_optarg ;; -*) as_fn_error $? "unrecognized option: \`$ac_option' Try \`$0 --help' for more information" ;; *=*) ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` # Reject names that are not valid shell variable names. case $ac_envvar in #( '' | [0-9]* | *[!_$as_cr_alnum]* ) as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; esac eval $ac_envvar=\$ac_optarg export $ac_envvar ;; *) # FIXME: should be removed in autoconf 3.0. $as_echo "$as_me: WARNING: you should use --build, --host, --target" >&2 expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && $as_echo "$as_me: WARNING: invalid host type: $ac_option" >&2 : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" ;; esac done if test -n "$ac_prev"; then ac_option=--`echo $ac_prev | sed 's/_/-/g'` as_fn_error $? "missing argument to $ac_option" fi if test -n "$ac_unrecognized_opts"; then case $enable_option_checking in no) ;; fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; *) $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; esac fi # Check all directory arguments for consistency. for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ datadir sysconfdir sharedstatedir localstatedir includedir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ libdir localedir mandir do eval ac_val=\$$ac_var # Remove trailing slashes. case $ac_val in */ ) ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` eval $ac_var=\$ac_val;; esac # Be sure to have absolute directory names. case $ac_val in [\\/$]* | ?:[\\/]* ) continue;; NONE | '' ) case $ac_var in *prefix ) continue;; esac;; esac as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" done # There might be people who depend on the old broken behavior: `$host' # used to hold the argument of --host etc. # FIXME: To remove some day. build=$build_alias host=$host_alias target=$target_alias # FIXME: To remove some day. if test "x$host_alias" != x; then if test "x$build_alias" = x; then cross_compiling=maybe $as_echo "$as_me: WARNING: if you wanted to set the --build type, don't use --host. If a cross compiler is detected then cross compile mode will be used" >&2 elif test "x$build_alias" != "x$host_alias"; then cross_compiling=yes fi fi ac_tool_prefix= test -n "$host_alias" && ac_tool_prefix=$host_alias- test "$silent" = yes && exec 6>/dev/null ac_pwd=`pwd` && test -n "$ac_pwd" && ac_ls_di=`ls -di .` && ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || as_fn_error $? "working directory cannot be determined" test "X$ac_ls_di" = "X$ac_pwd_ls_di" || as_fn_error $? "pwd does not report name of working directory" # Find the source files, if location was not specified. if test -z "$srcdir"; then ac_srcdir_defaulted=yes # Try the directory containing this script, then the parent directory. ac_confdir=`$as_dirname -- "$as_myself" || $as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_myself" : 'X\(//\)[^/]' \| \ X"$as_myself" : 'X\(//\)$' \| \ X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_myself" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` srcdir=$ac_confdir if test ! -r "$srcdir/$ac_unique_file"; then srcdir=.. fi else ac_srcdir_defaulted=no fi if test ! -r "$srcdir/$ac_unique_file"; then test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" fi ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" ac_abs_confdir=`( cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" pwd)` # When building in place, set srcdir=. if test "$ac_abs_confdir" = "$ac_pwd"; then srcdir=. fi # Remove unnecessary trailing slashes from srcdir. # Double slashes in file names in object file debugging info # mess up M-x gdb in Emacs. case $srcdir in */) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; esac for ac_var in $ac_precious_vars; do eval ac_env_${ac_var}_set=\${${ac_var}+set} eval ac_env_${ac_var}_value=\$${ac_var} eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} eval ac_cv_env_${ac_var}_value=\$${ac_var} done # # Report the --help message. # if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF \`configure' configures istgt 0.4 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... To assign environment variables (e.g., CC, CFLAGS...), specify them as VAR=VALUE. See below for descriptions of some of the useful variables. Defaults for the options are specified in brackets. Configuration: -h, --help display this help and exit --help=short display options specific to this package --help=recursive display the short help of all the included packages -V, --version display version information and exit -q, --quiet, --silent do not print \`checking ...' messages --cache-file=FILE cache test results in FILE [disabled] -C, --config-cache alias for \`--cache-file=config.cache' -n, --no-create do not create output files --srcdir=DIR find the sources in DIR [configure dir or \`..'] Installation directories: --prefix=PREFIX install architecture-independent files in PREFIX [$ac_default_prefix] --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX [PREFIX] By default, \`make install' will install all the files in \`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify an installation prefix other than \`$ac_default_prefix' using \`--prefix', for instance \`--prefix=\$HOME'. For better control, use the options below. Fine tuning of the installation directories: --bindir=DIR user executables [EPREFIX/bin] --sbindir=DIR system admin executables [EPREFIX/sbin] --libexecdir=DIR program executables [EPREFIX/libexec] --sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --localstatedir=DIR modifiable single-machine data [PREFIX/var] --libdir=DIR object code libraries [EPREFIX/lib] --includedir=DIR C header files [PREFIX/include] --oldincludedir=DIR C header files for non-gcc [/usr/include] --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] --datadir=DIR read-only architecture-independent data [DATAROOTDIR] --infodir=DIR info documentation [DATAROOTDIR/info] --localedir=DIR locale-dependent data [DATAROOTDIR/locale] --mandir=DIR man documentation [DATAROOTDIR/man] --docdir=DIR documentation root [DATAROOTDIR/doc/istgt] --htmldir=DIR html documentation [DOCDIR] --dvidir=DIR dvi documentation [DOCDIR] --pdfdir=DIR pdf documentation [DOCDIR] --psdir=DIR ps documentation [DOCDIR] _ACEOF cat <<\_ACEOF _ACEOF fi if test -n "$ac_init_help"; then case $ac_init_help in short | recursive ) echo "Configuration of istgt 0.4:";; esac cat <<\_ACEOF Optional Features: --disable-option-checking ignore unrecognized --enable/--with options --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no) --enable-FEATURE[=ARG] include FEATURE [ARG=yes] --disable-largefile omit support for large files --enable-debug enable debug(logging) Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no) --with-configdir=DIR (default $ac_default_prefix/etc/istgt) --with-mediadir=DIR (default /var/istgt) --with-logfacility syslog facility to log with (default "local7") --with-logpriority syslog priority to log with (default "info") Some influential environment variables: CC C compiler command CFLAGS C compiler flags LDFLAGS linker flags, e.g. -L if you have libraries in a nonstandard directory LIBS libraries to pass to the linker, e.g. -l CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if you have headers in a nonstandard directory CPP C preprocessor Use these variables to override the choices made by `configure' or to help it to find libraries and programs with nonstandard names/locations. Report bugs to the package provider. _ACEOF ac_status=$? fi if test "$ac_init_help" = "recursive"; then # If there are subdirs, report their specific --help. for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue test -d "$ac_dir" || { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || continue ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix cd "$ac_dir" || { ac_status=$?; continue; } # Check for guested configure. if test -f "$ac_srcdir/configure.gnu"; then echo && $SHELL "$ac_srcdir/configure.gnu" --help=recursive elif test -f "$ac_srcdir/configure"; then echo && $SHELL "$ac_srcdir/configure" --help=recursive else $as_echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2 fi || ac_status=$? cd "$ac_pwd" || { ac_status=$?; break; } done fi test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then cat <<\_ACEOF istgt configure 0.4 generated by GNU Autoconf 2.68 Copyright (C) 2010 Free Software Foundation, Inc. This configure script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it. _ACEOF exit fi ## ------------------------ ## ## Autoconf initialization. ## ## ------------------------ ## # ac_fn_c_try_compile LINENO # -------------------------- # Try to compile conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest.$ac_objext; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_compile # ac_fn_c_try_link LINENO # ----------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_link () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack rm -f conftest.$ac_objext conftest$ac_exeext if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { test -z "$ac_c_werror_flag" || test ! -s conftest.err } && test -s conftest$ac_exeext && { test "$cross_compiling" = yes || $as_test_x conftest$ac_exeext }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would # interfere with the next link command; also delete a directory that is # left behind by Apple's compiler. We do this before executing the actions. rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_link # ac_fn_c_try_cpp LINENO # ---------------------- # Try to preprocess conftest.$ac_ext, and return whether this succeeded. ac_fn_c_try_cpp () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_cpp conftest.$ac_ext" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_cpp conftest.$ac_ext") 2>conftest.err ac_status=$? if test -s conftest.err; then grep -v '^ *+' conftest.err >conftest.er1 cat conftest.er1 >&5 mv -f conftest.er1 conftest.err fi $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } > conftest.i && { test -z "$ac_c_preproc_warn_flag$ac_c_werror_flag" || test ! -s conftest.err }; then : ac_retval=0 else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=1 fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_cpp # ac_fn_c_check_header_mongrel LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists, giving a warning if it cannot be compiled using # the include files in INCLUDES and setting the cache variable VAR # accordingly. ac_fn_c_check_header_mongrel () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if eval \${$3+:} false; then : { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } else # Is the header compilable? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 usability" >&5 $as_echo_n "checking $2 usability... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_header_compiler=yes else ac_header_compiler=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_compiler" >&5 $as_echo "$ac_header_compiler" >&6; } # Is the header present? { $as_echo "$as_me:${as_lineno-$LINENO}: checking $2 presence" >&5 $as_echo_n "checking $2 presence... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include <$2> _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : ac_header_preproc=yes else ac_header_preproc=no fi rm -f conftest.err conftest.i conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_header_preproc" >&5 $as_echo "$ac_header_preproc" >&6; } # So? What about this header? case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in #(( yes:no: ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&5 $as_echo "$as_me: WARNING: $2: accepted by the compiler, rejected by the preprocessor!" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; no:yes:* ) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: present but cannot be compiled" >&5 $as_echo "$as_me: WARNING: $2: present but cannot be compiled" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: check for missing prerequisite headers?" >&5 $as_echo "$as_me: WARNING: $2: check for missing prerequisite headers?" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: see the Autoconf documentation" >&5 $as_echo "$as_me: WARNING: $2: see the Autoconf documentation" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&5 $as_echo "$as_me: WARNING: $2: section \"Present But Cannot Be Compiled\"" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $2: proceeding with the compiler's result" >&5 $as_echo "$as_me: WARNING: $2: proceeding with the compiler's result" >&2;} ;; esac { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=\$ac_header_compiler" fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } fi eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_mongrel # ac_fn_c_try_run LINENO # ---------------------- # Try to link conftest.$ac_ext, and return whether this succeeded. Assumes # that executables *can* be run. ac_fn_c_try_run () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } && { ac_try='./conftest$ac_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then : ac_retval=0 else $as_echo "$as_me: program exited with status $ac_status" >&5 $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 ac_retval=$ac_status fi rm -rf conftest.dSYM conftest_ipa8_conftest.oo eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno as_fn_set_status $ac_retval } # ac_fn_c_try_run # ac_fn_c_check_header_compile LINENO HEADER VAR INCLUDES # ------------------------------------------------------- # Tests whether HEADER exists and can be compiled using the include files in # INCLUDES, setting the cache variable VAR accordingly. ac_fn_c_check_header_compile () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 #include <$2> _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_header_compile # ac_fn_c_find_intX_t LINENO BITS VAR # ----------------------------------- # Finds a signed integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_intX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for int$2_t" >&5 $as_echo_n "checking for int$2_t... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in int$2_t 'int' 'long int' \ 'long long int' 'short int' 'signed char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main () { static int test_array [1 - 2 * !(0 < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1))]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default enum { N = $2 / 2 - 1 }; int main () { static int test_array [1 - 2 * !(($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 1) < ($ac_type) ((((($ac_type) 1 << N) << N) - 1) * 2 + 2))]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else case $ac_type in #( int$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if eval test \"x\$"$3"\" = x"no"; then : else break fi done fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_intX_t # ac_fn_c_check_type LINENO TYPE VAR INCLUDES # ------------------------------------------- # Tests whether TYPE exists after having included INCLUDES, setting cache # variable VAR accordingly. ac_fn_c_check_type () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof ($2)) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $4 int main () { if (sizeof (($2))) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else eval "$3=yes" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_type # ac_fn_c_check_member LINENO AGGR MEMBER VAR INCLUDES # ---------------------------------------------------- # Tries to find if the field MEMBER exists in type AGGR, after including # INCLUDES, setting cache variable VAR accordingly. ac_fn_c_check_member () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2.$3" >&5 $as_echo_n "checking for $2.$3... " >&6; } if eval \${$4+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $5 int main () { static $2 ac_aggr; if (sizeof ac_aggr.$3) return 0; ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : eval "$4=yes" else eval "$4=no" fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi eval ac_res=\$$4 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_member # ac_fn_c_find_uintX_t LINENO BITS VAR # ------------------------------------ # Finds an unsigned integer type with width BITS, setting cache variable VAR # accordingly. ac_fn_c_find_uintX_t () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for uint$2_t" >&5 $as_echo_n "checking for uint$2_t... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else eval "$3=no" # Order is important - never check a type that is potentially smaller # than half of the expected target width. for ac_type in uint$2_t 'unsigned int' 'unsigned long int' \ 'unsigned long long int' 'unsigned short int' 'unsigned char'; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { static int test_array [1 - 2 * !((($ac_type) -1 >> ($2 / 2 - 1)) >> ($2 / 2 - 1) == 3)]; test_array [0] = 0 ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : case $ac_type in #( uint$2_t) : eval "$3=yes" ;; #( *) : eval "$3=\$ac_type" ;; esac fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if eval test \"x\$"$3"\" = x"no"; then : else break fi done fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_find_uintX_t # ac_fn_c_check_func LINENO FUNC VAR # ---------------------------------- # Tests whether FUNC exists, setting the cache variable VAR accordingly ac_fn_c_check_func () { as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 $as_echo_n "checking for $2... " >&6; } if eval \${$3+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Define $2 to an innocuous variant, in case declares $2. For example, HP-UX 11i declares gettimeofday. */ #define $2 innocuous_$2 /* System header to define __stub macros and hopefully few prototypes, which can conflict with char $2 (); below. Prefer to if __STDC__ is defined, since exists even on freestanding compilers. */ #ifdef __STDC__ # include #else # include #endif #undef $2 /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char $2 (); /* The GNU C library defines this for functions which it implements to always fail with ENOSYS. Some functions are actually named something starting with __ and the normal name is an alias. */ #if defined __stub_$2 || defined __stub___$2 choke me #endif int main () { return $2 (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : eval "$3=yes" else eval "$3=no" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi eval ac_res=\$$3 { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 $as_echo "$ac_res" >&6; } eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno } # ac_fn_c_check_func cat >config.log <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. It was created by istgt $as_me 0.4, which was generated by GNU Autoconf 2.68. Invocation command line was $ $0 $@ _ACEOF exec 5>>config.log { cat <<_ASUNAME ## --------- ## ## Platform. ## ## --------- ## hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` uname -m = `(uname -m) 2>/dev/null || echo unknown` uname -r = `(uname -r) 2>/dev/null || echo unknown` uname -s = `(uname -s) 2>/dev/null || echo unknown` uname -v = `(uname -v) 2>/dev/null || echo unknown` /usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` /bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` /bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` /usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` /usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` /usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` /bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` /usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` /bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` _ASUNAME as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. $as_echo "PATH: $as_dir" done IFS=$as_save_IFS } >&5 cat >&5 <<_ACEOF ## ----------- ## ## Core tests. ## ## ----------- ## _ACEOF # Keep a trace of the command line. # Strip out --no-create and --no-recursion so they do not pile up. # Strip out --silent because we don't want to record it for future runs. # Also quote any args containing shell meta-characters. # Make two passes to allow for proper duplicate-argument suppression. ac_configure_args= ac_configure_args0= ac_configure_args1= ac_must_keep_next=false for ac_pass in 1 2 do for ac_arg do case $ac_arg in -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil) continue ;; *\'*) ac_arg=`$as_echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; esac case $ac_pass in 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; 2) as_fn_append ac_configure_args1 " '$ac_arg'" if test $ac_must_keep_next = true; then ac_must_keep_next=false # Got value, back to normal. else case $ac_arg in *=* | --config-cache | -C | -disable-* | --disable-* \ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ | -with-* | --with-* | -without-* | --without-* | --x) case "$ac_configure_args0 " in "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; esac ;; -* ) ac_must_keep_next=true ;; esac fi as_fn_append ac_configure_args " '$ac_arg'" ;; esac done done { ac_configure_args0=; unset ac_configure_args0;} { ac_configure_args1=; unset ac_configure_args1;} # When interrupted or exit'd, cleanup temporary files, and complete # config.log. We remove comments because anyway the quotes in there # would cause problems or look ugly. # WARNING: Use '\'' to represent an apostrophe within the trap. # WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. trap 'exit_status=$? # Save into config.log some information that might help in debugging. { echo $as_echo "## ---------------- ## ## Cache variables. ## ## ---------------- ##" echo # The following way of writing the cache mishandles newlines in values, ( for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( *${as_nl}ac_space=\ *) sed -n \ "s/'\''/'\''\\\\'\'''\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" ;; #( *) sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) echo $as_echo "## ----------------- ## ## Output variables. ## ## ----------------- ##" echo for ac_var in $ac_subst_vars do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo if test -n "$ac_subst_files"; then $as_echo "## ------------------- ## ## File substitutions. ## ## ------------------- ##" echo for ac_var in $ac_subst_files do eval ac_val=\$$ac_var case $ac_val in *\'\''*) ac_val=`$as_echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; esac $as_echo "$ac_var='\''$ac_val'\''" done | sort echo fi if test -s confdefs.h; then $as_echo "## ----------- ## ## confdefs.h. ## ## ----------- ##" echo cat confdefs.h echo fi test "$ac_signal" != 0 && $as_echo "$as_me: caught signal $ac_signal" $as_echo "$as_me: exit $exit_status" } >&5 rm -f core *.core core.conftest.* && rm -f -r conftest* confdefs* conf$$* $ac_clean_files && exit $exit_status ' 0 for ac_signal in 1 2 13 15; do trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal done ac_signal=0 # confdefs.h avoids OS command line length limits that DEFS can exceed. rm -f -r conftest* confdefs.h $as_echo "/* confdefs.h */" > confdefs.h # Predefined preprocessor variables. cat >>confdefs.h <<_ACEOF #define PACKAGE_NAME "$PACKAGE_NAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_TARNAME "$PACKAGE_TARNAME" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_VERSION "$PACKAGE_VERSION" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_STRING "$PACKAGE_STRING" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT" _ACEOF cat >>confdefs.h <<_ACEOF #define PACKAGE_URL "$PACKAGE_URL" _ACEOF # Let the site file select an alternate cache file if it wants to. # Prefer an explicitly selected file to automatically selected ones. ac_site_file1=NONE ac_site_file2=NONE if test -n "$CONFIG_SITE"; then # We do not want a PATH search for config.site. case $CONFIG_SITE in #(( -*) ac_site_file1=./$CONFIG_SITE;; */*) ac_site_file1=$CONFIG_SITE;; *) ac_site_file1=./$CONFIG_SITE;; esac elif test "x$prefix" != xNONE; then ac_site_file1=$prefix/share/config.site ac_site_file2=$prefix/etc/config.site else ac_site_file1=$ac_default_prefix/share/config.site ac_site_file2=$ac_default_prefix/etc/config.site fi for ac_site_file in "$ac_site_file1" "$ac_site_file2" do test "x$ac_site_file" = xNONE && continue if test /dev/null != "$ac_site_file" && test -r "$ac_site_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 $as_echo "$as_me: loading site script $ac_site_file" >&6;} sed 's/^/| /' "$ac_site_file" >&5 . "$ac_site_file" \ || { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "failed to load site script $ac_site_file See \`config.log' for more details" "$LINENO" 5; } fi done if test -r "$cache_file"; then # Some versions of bash will fail to source /dev/null (special files # actually), so we avoid doing that. DJGPP emulates it as a regular file. if test /dev/null != "$cache_file" && test -f "$cache_file"; then { $as_echo "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 $as_echo "$as_me: loading cache $cache_file" >&6;} case $cache_file in [\\/]* | ?:[\\/]* ) . "$cache_file";; *) . "./$cache_file";; esac fi else { $as_echo "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 $as_echo "$as_me: creating cache $cache_file" >&6;} >$cache_file fi # Check that the precious variables saved in the cache have kept the same # value. ac_cache_corrupted=false for ac_var in $ac_precious_vars; do eval ac_old_set=\$ac_cv_env_${ac_var}_set eval ac_new_set=\$ac_env_${ac_var}_set eval ac_old_val=\$ac_cv_env_${ac_var}_value eval ac_new_val=\$ac_env_${ac_var}_value case $ac_old_set,$ac_new_set in set,) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} ac_cache_corrupted=: ;; ,set) { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 $as_echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} ac_cache_corrupted=: ;; ,);; *) if test "x$ac_old_val" != "x$ac_new_val"; then # differences in whitespace do not lead to failure. ac_old_val_w=`echo x $ac_old_val` ac_new_val_w=`echo x $ac_new_val` if test "$ac_old_val_w" != "$ac_new_val_w"; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 $as_echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} ac_cache_corrupted=: else { $as_echo "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 $as_echo "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} eval $ac_var=\$ac_old_val fi { $as_echo "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 $as_echo "$as_me: former value: \`$ac_old_val'" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 $as_echo "$as_me: current value: \`$ac_new_val'" >&2;} fi;; esac # Pass precious variables to config.status. if test "$ac_new_set" = set; then case $ac_new_val in *\'*) ac_arg=$ac_var=`$as_echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; *) ac_arg=$ac_var=$ac_new_val ;; esac case " $ac_configure_args " in *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. *) as_fn_append ac_configure_args " '$ac_arg'" ;; esac fi done if $ac_cache_corrupted; then { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} { $as_echo "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 $as_echo "$as_me: error: changes in the environment can compromise the build" >&2;} as_fn_error $? "run \`make distclean' and/or \`rm $cache_file' and start over" "$LINENO" 5 fi ## -------------------- ## ## Main body of script. ## ## -------------------- ## ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_config_headers="$ac_config_headers src/config.h" # Checks for programs. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. set dummy ${ac_tool_prefix}gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_CC"; then ac_ct_CC=$CC # Extract the first word of "gcc", so it can be a program name with args. set dummy gcc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="gcc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi else CC="$ac_cv_prog_CC" fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. set dummy ${ac_tool_prefix}cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="${ac_tool_prefix}cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi fi if test -z "$CC"; then # Extract the first word of "cc", so it can be a program name with args. set dummy cc; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else ac_prog_rejected=no as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then if test "$as_dir/$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then ac_prog_rejected=yes continue fi ac_cv_prog_CC="cc" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS if test $ac_prog_rejected = yes; then # We found a bogon in the path, so make sure we never use it. set dummy $ac_cv_prog_CC shift if test $# != 0; then # We chose a different compiler from the bogus one. # However, it has the same basename, so the bogon will be chosen # first if we set CC to just the basename; use the full file name. shift ac_cv_prog_CC="$as_dir/$ac_word${1+' '}$@" fi fi fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$CC"; then if test -n "$ac_tool_prefix"; then for ac_prog in cl.exe do # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. set dummy $ac_tool_prefix$ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$CC"; then ac_cv_prog_CC="$CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_CC="$ac_tool_prefix$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi CC=$ac_cv_prog_CC if test -n "$CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 $as_echo "$CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$CC" && break done fi if test -z "$CC"; then ac_ct_CC=$CC for ac_prog in cl.exe do # Extract the first word of "$ac_prog", so it can be a program name with args. set dummy $ac_prog; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_CC+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_CC"; then ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_CC="$ac_prog" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_CC=$ac_cv_prog_ac_ct_CC if test -n "$ac_ct_CC"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 $as_echo "$ac_ct_CC" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi test -n "$ac_ct_CC" && break done if test "x$ac_ct_CC" = x; then CC="" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac CC=$ac_ct_CC fi fi fi test -z "$CC" && { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "no acceptable C compiler found in \$PATH See \`config.log' for more details" "$LINENO" 5; } # Provide some information about the compiler. $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 set X $ac_compile ac_compiler=$2 for ac_option in --version -v -V -qversion; do { { ac_try="$ac_compiler $ac_option >&5" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compiler $ac_option >&5") 2>conftest.err ac_status=$? if test -s conftest.err; then sed '10a\ ... rest of stderr output deleted ... 10q' conftest.err >conftest.er1 cat conftest.er1 >&5 fi rm -f conftest.er1 conftest.err $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } done cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" # Try to create an executable without -o first, disregard a.out. # It will help us diagnose broken compilers, and finding out an intuition # of exeext. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 $as_echo_n "checking whether the C compiler works... " >&6; } ac_link_default=`$as_echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'` # The possible output files: ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" ac_rmfiles= for ac_file in $ac_files do case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; * ) ac_rmfiles="$ac_rmfiles $ac_file";; esac done rm -f $ac_rmfiles if { { ac_try="$ac_link_default" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link_default") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. # So ignore a value of `no', otherwise this would lead to `EXEEXT = no' # in a Makefile. We should not override ac_cv_exeext if it was cached, # so that the user can short-circuit this test for compilers unknown to # Autoconf. for ac_file in $ac_files '' do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; [ab].out ) # We found the default executable, but exeext='' is most # certainly right. break;; *.* ) if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no; then :; else ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` fi # We set ac_cv_exeext here because the later test for it is not # safe: cross compilers may not add the suffix if given an `-o' # argument, so we may need to know it at that point already. # Even if this section looks crufty: it has the advantage of # actually working. break;; * ) break;; esac done test "$ac_cv_exeext" = no && ac_cv_exeext= else ac_file='' fi if test -z "$ac_file"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error 77 "C compiler cannot create executables See \`config.log' for more details" "$LINENO" 5; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 $as_echo_n "checking for C compiler default output file name... " >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 $as_echo "$ac_file" >&6; } ac_exeext=$ac_cv_exeext rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 $as_echo_n "checking for suffix of executables... " >&6; } if { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : # If both `conftest.exe' and `conftest' are `present' (well, observable) # catch `conftest.exe'. For instance with Cygwin, `ls conftest' will # work properly (i.e., refer to `conftest.exe'), while it won't with # `rm'. for ac_file in conftest.exe conftest conftest.*; do test -f "$ac_file" || continue case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` break;; * ) break;; esac done else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of executables: cannot compile and link See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest conftest$ac_cv_exeext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 $as_echo "$ac_cv_exeext" >&6; } rm -f conftest.$ac_ext EXEEXT=$ac_cv_exeext ac_exeext=$EXEEXT cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include int main () { FILE *f = fopen ("conftest.out", "w"); return ferror (f) || fclose (f) != 0; ; return 0; } _ACEOF ac_clean_files="$ac_clean_files conftest.out" # Check that the compiler produces executables we can run. If not, either # the compiler is broken, or we cross compile. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 $as_echo_n "checking whether we are cross compiling... " >&6; } if test "$cross_compiling" != yes; then { { ac_try="$ac_link" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_link") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; } if { ac_try='./conftest$ac_cv_exeext' { { case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_try") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; }; then cross_compiling=no else if test "$cross_compiling" = maybe; then cross_compiling=yes else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot run C compiled programs. If you meant to cross compile, use \`--host'. See \`config.log' for more details" "$LINENO" 5; } fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 $as_echo "$cross_compiling" >&6; } rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out ac_clean_files=$ac_clean_files_save { $as_echo "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 $as_echo_n "checking for suffix of object files... " >&6; } if ${ac_cv_objext+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF rm -f conftest.o conftest.obj if { { ac_try="$ac_compile" case "(($ac_try" in *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; *) ac_try_echo=$ac_try;; esac eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" $as_echo "$ac_try_echo"; } >&5 (eval "$ac_compile") 2>&5 ac_status=$? $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 test $ac_status = 0; }; then : for ac_file in conftest.o conftest.obj conftest.*; do test -f "$ac_file" || continue; case $ac_file in *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` break;; esac done else $as_echo "$as_me: failed program was:" >&5 sed 's/^/| /' conftest.$ac_ext >&5 { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "cannot compute suffix of object files: cannot compile See \`config.log' for more details" "$LINENO" 5; } fi rm -f conftest.$ac_cv_objext conftest.$ac_ext fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 $as_echo "$ac_cv_objext" >&6; } OBJEXT=$ac_cv_objext ac_objext=$OBJEXT { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether we are using the GNU C compiler" >&5 $as_echo_n "checking whether we are using the GNU C compiler... " >&6; } if ${ac_cv_c_compiler_gnu+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { #ifndef __GNUC__ choke me #endif ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_compiler_gnu=yes else ac_compiler_gnu=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_c_compiler_gnu=$ac_compiler_gnu fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 $as_echo "$ac_cv_c_compiler_gnu" >&6; } if test $ac_compiler_gnu = yes; then GCC=yes else GCC= fi ac_test_CFLAGS=${CFLAGS+set} ac_save_CFLAGS=$CFLAGS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 $as_echo_n "checking whether $CC accepts -g... " >&6; } if ${ac_cv_prog_cc_g+:} false; then : $as_echo_n "(cached) " >&6 else ac_save_c_werror_flag=$ac_c_werror_flag ac_c_werror_flag=yes ac_cv_prog_cc_g=no CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes else CFLAGS="" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : else ac_c_werror_flag=$ac_save_c_werror_flag CFLAGS="-g" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_g=yes fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_c_werror_flag=$ac_save_c_werror_flag fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 $as_echo "$ac_cv_prog_cc_g" >&6; } if test "$ac_test_CFLAGS" = set; then CFLAGS=$ac_save_CFLAGS elif test $ac_cv_prog_cc_g = yes; then if test "$GCC" = yes; then CFLAGS="-g -O2" else CFLAGS="-g" fi else if test "$GCC" = yes; then CFLAGS="-O2" else CFLAGS= fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $CC option to accept ISO C89" >&5 $as_echo_n "checking for $CC option to accept ISO C89... " >&6; } if ${ac_cv_prog_cc_c89+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_prog_cc_c89=no ac_save_CC=$CC cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include /* Most of the following tests are stolen from RCS 5.7's src/conf.sh. */ struct buf { int x; }; FILE * (*rcsopen) (struct buf *, struct stat *, int); static char *e (p, i) char **p; int i; { return p[i]; } static char *f (char * (*g) (char **, int), char **p, ...) { char *s; va_list v; va_start (v,p); s = g (p, va_arg (v,int)); va_end (v); return s; } /* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has function prototypes and stuff, but not '\xHH' hex character constants. These don't provoke an error unfortunately, instead are silently treated as 'x'. The following induces an error, until -std is added to get proper ANSI mode. Curiously '\x00'!='x' always comes out true, for an array size at least. It's necessary to write '\x00'==0 to get something that's true only with -std. */ int osf4_cc_array ['\x00' == 0 ? 1 : -1]; /* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters inside strings and character constants. */ #define FOO(x) 'x' int xlc6_cc_array[FOO(a) == 'x' ? 1 : -1]; int test (int i, double x); struct s1 {int (*f) (int a);}; struct s2 {int (*f) (double a);}; int pairnames (int, char **, FILE *(*)(struct buf *, struct stat *, int), int, int); int argc; char **argv; int main () { return f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]; ; return 0; } _ACEOF for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std \ -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" do CC="$ac_save_CC $ac_arg" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_prog_cc_c89=$ac_arg fi rm -f core conftest.err conftest.$ac_objext test "x$ac_cv_prog_cc_c89" != "xno" && break done rm -f conftest.$ac_ext CC=$ac_save_CC fi # AC_CACHE_VAL case "x$ac_cv_prog_cc_c89" in x) { $as_echo "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 $as_echo "none needed" >&6; } ;; xno) { $as_echo "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 $as_echo "unsupported" >&6; } ;; *) CC="$CC $ac_cv_prog_cc_c89" { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 $as_echo "$ac_cv_prog_cc_c89" >&6; } ;; esac if test "x$ac_cv_prog_cc_c89" != xno; then : fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu ac_aux_dir= for ac_dir in "$srcdir" "$srcdir/.." "$srcdir/../.."; do if test -f "$ac_dir/install-sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install-sh -c" break elif test -f "$ac_dir/install.sh"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/install.sh -c" break elif test -f "$ac_dir/shtool"; then ac_aux_dir=$ac_dir ac_install_sh="$ac_aux_dir/shtool install -c" break fi done if test -z "$ac_aux_dir"; then as_fn_error $? "cannot find install-sh, install.sh, or shtool in \"$srcdir\" \"$srcdir/..\" \"$srcdir/../..\"" "$LINENO" 5 fi # These three variables are undocumented and unsupported, # and are intended to be withdrawn in a future Autoconf release. # They can cause serious problems if a builder's source tree is in a directory # whose full name contains unusual characters. ac_config_guess="$SHELL $ac_aux_dir/config.guess" # Please don't use this var. ac_config_sub="$SHELL $ac_aux_dir/config.sub" # Please don't use this var. ac_configure="$SHELL $ac_aux_dir/configure" # Please don't use this var. # Find a good install program. We prefer a C program (faster), # so one script is as good as another. But avoid the broken or # incompatible versions: # SysV /etc/install, /usr/sbin/install # SunOS /usr/etc/install # IRIX /sbin/install # AIX /bin/install # AmigaOS /C/install, which installs bootblocks on floppy discs # AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag # AFS /usr/afsws/bin/install, which mishandles nonexistent args # SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff" # OS/2's system install, which has a completely different semantic # ./install, which can be erroneously created by make from ./install.sh. # Reject install programs that cannot install multiple files. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a BSD-compatible install" >&5 $as_echo_n "checking for a BSD-compatible install... " >&6; } if test -z "$INSTALL"; then if ${ac_cv_path_install+:} false; then : $as_echo_n "(cached) " >&6 else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. # Account for people who put trailing slashes in PATH elements. case $as_dir/ in #(( ./ | .// | /[cC]/* | \ /etc/* | /usr/sbin/* | /usr/etc/* | /sbin/* | /usr/afsws/bin/* | \ ?:[\\/]os2[\\/]install[\\/]* | ?:[\\/]OS2[\\/]INSTALL[\\/]* | \ /usr/ucb/* ) ;; *) # OSF1 and SCO ODT 3.0 have their own names for install. # Don't use installbsd from OSF since it installs stuff as root # by default. for ac_prog in ginstall scoinst install; do for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_prog$ac_exec_ext" && $as_test_x "$as_dir/$ac_prog$ac_exec_ext"; }; then if test $ac_prog = install && grep dspmsg "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # AIX install. It has an incompatible calling convention. : elif test $ac_prog = install && grep pwplus "$as_dir/$ac_prog$ac_exec_ext" >/dev/null 2>&1; then # program-specific install script used by HP pwplus--don't use. : else rm -rf conftest.one conftest.two conftest.dir echo one > conftest.one echo two > conftest.two mkdir conftest.dir if "$as_dir/$ac_prog$ac_exec_ext" -c conftest.one conftest.two "`pwd`/conftest.dir" && test -s conftest.one && test -s conftest.two && test -s conftest.dir/conftest.one && test -s conftest.dir/conftest.two then ac_cv_path_install="$as_dir/$ac_prog$ac_exec_ext -c" break 3 fi fi fi done done ;; esac done IFS=$as_save_IFS rm -rf conftest.one conftest.two conftest.dir fi if test "${ac_cv_path_install+set}" = set; then INSTALL=$ac_cv_path_install else # As a last resort, use the slow shell script. Don't cache a # value for INSTALL within a source directory, because that will # break other packages using the cache if that directory is # removed, or if the value is a relative name. INSTALL=$ac_install_sh fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $INSTALL" >&5 $as_echo "$INSTALL" >&6; } # Use test -z because SunOS4 sh mishandles braces in ${var-val}. # It thinks the first close brace ends the variable substitution. test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}' test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL}' test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644' if test -n "$ac_tool_prefix"; then # Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args. set dummy ${ac_tool_prefix}ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$RANLIB"; then ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi RANLIB=$ac_cv_prog_RANLIB if test -n "$RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $RANLIB" >&5 $as_echo "$RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi fi if test -z "$ac_cv_prog_RANLIB"; then ac_ct_RANLIB=$RANLIB # Extract the first word of "ranlib", so it can be a program name with args. set dummy ranlib; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_prog_ac_ct_RANLIB+:} false; then : $as_echo_n "(cached) " >&6 else if test -n "$ac_ct_RANLIB"; then ac_cv_prog_ac_ct_RANLIB="$ac_ct_RANLIB" # Let the user override the test. else as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_prog_ac_ct_RANLIB="ranlib" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS fi fi ac_ct_RANLIB=$ac_cv_prog_ac_ct_RANLIB if test -n "$ac_ct_RANLIB"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_ct_RANLIB" >&5 $as_echo "$ac_ct_RANLIB" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi if test "x$ac_ct_RANLIB" = x; then RANLIB=":" else case $cross_compiling:$ac_tool_warned in yes:) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 $as_echo "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} ac_tool_warned=yes ;; esac RANLIB=$ac_ct_RANLIB fi else RANLIB="$ac_cv_prog_RANLIB" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for a sed that does not truncate output" >&5 $as_echo_n "checking for a sed that does not truncate output... " >&6; } if ${ac_cv_path_SED+:} false; then : $as_echo_n "(cached) " >&6 else ac_script=s/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/ for ac_i in 1 2 3 4 5 6 7; do ac_script="$ac_script$as_nl$ac_script" done echo "$ac_script" 2>/dev/null | sed 99q >conftest.sed { ac_script=; unset ac_script;} if test -z "$SED"; then ac_path_SED_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in sed gsed; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_SED="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_SED" && $as_test_x "$ac_path_SED"; } || continue # Check for GNU ac_path_SED and select it if it is found. # Check for GNU $ac_path_SED case `"$ac_path_SED" --version 2>&1` in *GNU*) ac_cv_path_SED="$ac_path_SED" ac_path_SED_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo '' >> "conftest.nl" "$ac_path_SED" -f conftest.sed < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_SED_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_SED="$ac_path_SED" ac_path_SED_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_SED_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_SED"; then as_fn_error $? "no acceptable sed could be found in \$PATH" "$LINENO" 5 fi else ac_cv_path_SED=$SED fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_SED" >&5 $as_echo "$ac_cv_path_SED" >&6; } SED="$ac_cv_path_SED" rm -f conftest.sed # Checks for libraries. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for pthread_create in -lpthread" >&5 $as_echo_n "checking for pthread_create in -lpthread... " >&6; } if ${ac_cv_lib_pthread_pthread_create+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lpthread $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char pthread_create (); int main () { return pthread_create (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_pthread_pthread_create=yes else ac_cv_lib_pthread_pthread_create=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_pthread_pthread_create" >&5 $as_echo "$ac_cv_lib_pthread_pthread_create" >&6; } if test "x$ac_cv_lib_pthread_pthread_create" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBPTHREAD 1 _ACEOF LIBS="-lpthread $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for MD5_Update in -lcrypto" >&5 $as_echo_n "checking for MD5_Update in -lcrypto... " >&6; } if ${ac_cv_lib_crypto_MD5_Update+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcrypto $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char MD5_Update (); int main () { return MD5_Update (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_crypto_MD5_Update=yes else ac_cv_lib_crypto_MD5_Update=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_crypto_MD5_Update" >&5 $as_echo "$ac_cv_lib_crypto_MD5_Update" >&6; } if test "x$ac_cv_lib_crypto_MD5_Update" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBCRYPTO 1 _ACEOF LIBS="-lcrypto $LIBS" fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for cam_open_spec_device in -lcam" >&5 $as_echo_n "checking for cam_open_spec_device in -lcam... " >&6; } if ${ac_cv_lib_cam_cam_open_spec_device+:} false; then : $as_echo_n "(cached) " >&6 else ac_check_lib_save_LIBS=$LIBS LIBS="-lcam $LIBS" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ /* Override any GCC internal prototype to avoid an error. Use char because int might match the return type of a GCC builtin and then its argument prototype would still apply. */ #ifdef __cplusplus extern "C" #endif char cam_open_spec_device (); int main () { return cam_open_spec_device (); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : ac_cv_lib_cam_cam_open_spec_device=yes else ac_cv_lib_cam_cam_open_spec_device=no fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext LIBS=$ac_check_lib_save_LIBS fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_cam_cam_open_spec_device" >&5 $as_echo "$ac_cv_lib_cam_cam_open_spec_device" >&6; } if test "x$ac_cv_lib_cam_cam_open_spec_device" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_LIBCAM 1 _ACEOF LIBS="-lcam $LIBS" fi # Checks for header files. ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to run the C preprocessor" >&5 $as_echo_n "checking how to run the C preprocessor... " >&6; } # On Suns, sometimes $CPP names a directory. if test -n "$CPP" && test -d "$CPP"; then CPP= fi if test -z "$CPP"; then if ${ac_cv_prog_CPP+:} false; then : $as_echo_n "(cached) " >&6 else # Double quotes because CPP needs to be expanded for CPP in "$CC -E" "$CC -E -traditional-cpp" "/lib/cpp" do ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : break fi done ac_cv_prog_CPP=$CPP fi CPP=$ac_cv_prog_CPP else ac_cv_prog_CPP=$CPP fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $CPP" >&5 $as_echo "$CPP" >&6; } ac_preproc_ok=false for ac_c_preproc_warn_flag in '' yes do # Use a header file that comes with gcc, so configuring glibc # with a fresh cross-compiler works. # Prefer to if __STDC__ is defined, since # exists even on freestanding compilers. # On the NeXT, cc -E runs the code through the compiler's parser, # not just through cpp. "Syntax error" is here to catch this case. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifdef __STDC__ # include #else # include #endif Syntax error _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : else # Broken: fails on valid input. continue fi rm -f conftest.err conftest.i conftest.$ac_ext # OK, works on sane cases. Now check whether nonexistent headers # can be detected and how. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if ac_fn_c_try_cpp "$LINENO"; then : # Broken: success on invalid input. continue else # Passes both tests. ac_preproc_ok=: break fi rm -f conftest.err conftest.i conftest.$ac_ext done # Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped. rm -f conftest.i conftest.err conftest.$ac_ext if $ac_preproc_ok; then : else { { $as_echo "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 $as_echo "$as_me: error: in \`$ac_pwd':" >&2;} as_fn_error $? "C preprocessor \"$CPP\" fails sanity check See \`config.log' for more details" "$LINENO" 5; } fi ac_ext=c ac_cpp='$CPP $CPPFLAGS' ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' ac_compiler_gnu=$ac_cv_c_compiler_gnu { $as_echo "$as_me:${as_lineno-$LINENO}: checking for grep that handles long lines and -e" >&5 $as_echo_n "checking for grep that handles long lines and -e... " >&6; } if ${ac_cv_path_GREP+:} false; then : $as_echo_n "(cached) " >&6 else if test -z "$GREP"; then ac_path_GREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in grep ggrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue # Check for GNU ac_path_GREP and select it if it is found. # Check for GNU $ac_path_GREP case `"$ac_path_GREP" --version 2>&1` in *GNU*) ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'GREP' >> "conftest.nl" "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_GREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_GREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_GREP"; then as_fn_error $? "no acceptable grep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_GREP=$GREP fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_GREP" >&5 $as_echo "$ac_cv_path_GREP" >&6; } GREP="$ac_cv_path_GREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for egrep" >&5 $as_echo_n "checking for egrep... " >&6; } if ${ac_cv_path_EGREP+:} false; then : $as_echo_n "(cached) " >&6 else if echo a | $GREP -E '(a|b)' >/dev/null 2>&1 then ac_cv_path_EGREP="$GREP -E" else if test -z "$EGREP"; then ac_path_EGREP_found=false # Loop through the user's path and test for each of PROGNAME-LIST as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_prog in egrep; do for ac_exec_ext in '' $ac_executable_extensions; do ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext" { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue # Check for GNU ac_path_EGREP and select it if it is found. # Check for GNU $ac_path_EGREP case `"$ac_path_EGREP" --version 2>&1` in *GNU*) ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;; *) ac_count=0 $as_echo_n 0123456789 >"conftest.in" while : do cat "conftest.in" "conftest.in" >"conftest.tmp" mv "conftest.tmp" "conftest.in" cp "conftest.in" "conftest.nl" $as_echo 'EGREP' >> "conftest.nl" "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break as_fn_arith $ac_count + 1 && ac_count=$as_val if test $ac_count -gt ${ac_path_EGREP_max-0}; then # Best one so far, save it but keep looking for a better one ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_max=$ac_count fi # 10*(2^10) chars as input seems more than enough test $ac_count -gt 10 && break done rm -f conftest.in conftest.tmp conftest.nl conftest.out;; esac $ac_path_EGREP_found && break 3 done done done IFS=$as_save_IFS if test -z "$ac_cv_path_EGREP"; then as_fn_error $? "no acceptable egrep could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" "$LINENO" 5 fi else ac_cv_path_EGREP=$EGREP fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_path_EGREP" >&5 $as_echo "$ac_cv_path_EGREP" >&6; } EGREP="$ac_cv_path_EGREP" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for ANSI C header files" >&5 $as_echo_n "checking for ANSI C header files... " >&6; } if ${ac_cv_header_stdc+:} false; then : $as_echo_n "(cached) " >&6 else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #include #include int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_header_stdc=yes else ac_cv_header_stdc=no fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext if test $ac_cv_header_stdc = yes; then # SunOS 4.x string.h does not declare mem*, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "memchr" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include _ACEOF if (eval "$ac_cpp conftest.$ac_ext") 2>&5 | $EGREP "free" >/dev/null 2>&1; then : else ac_cv_header_stdc=no fi rm -f conftest* fi if test $ac_cv_header_stdc = yes; then # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi. if test "$cross_compiling" = yes; then : : else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include #include #if ((' ' & 0x0FF) == 0x020) # define ISLOWER(c) ('a' <= (c) && (c) <= 'z') # define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c)) #else # define ISLOWER(c) \ (('a' <= (c) && (c) <= 'i') \ || ('j' <= (c) && (c) <= 'r') \ || ('s' <= (c) && (c) <= 'z')) # define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c)) #endif #define XOR(e, f) (((e) && !(f)) || (!(e) && (f))) int main () { int i; for (i = 0; i < 256; i++) if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) return 2; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : else ac_cv_header_stdc=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_header_stdc" >&5 $as_echo "$ac_cv_header_stdc" >&6; } if test $ac_cv_header_stdc = yes; then $as_echo "#define STDC_HEADERS 1" >>confdefs.h fi # On IRIX 5.3, sys/types and inttypes.h are conflicting. for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \ inttypes.h stdint.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_compile "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default " if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in arpa/inet.h fcntl.h inttypes.h limits.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/ioctl.h sys/param.h sys/socket.h sys/time.h syslog.h unistd.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done # Checks for typedefs, structures, and compiler characteristics. { $as_echo "$as_me:${as_lineno-$LINENO}: checking for inline" >&5 $as_echo_n "checking for inline... " >&6; } if ${ac_cv_c_inline+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_c_inline=no for ac_kw in inline __inline__ __inline; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #ifndef __cplusplus typedef int foo_t; static $ac_kw foo_t static_foo () {return 0; } $ac_kw foo_t foo () {return 0; } #endif _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_c_inline=$ac_kw fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext test "$ac_cv_c_inline" != no && break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_inline" >&5 $as_echo "$ac_cv_c_inline" >&6; } case $ac_cv_c_inline in inline | yes) ;; *) case $ac_cv_c_inline in no) ac_val=;; *) ac_val=$ac_cv_c_inline;; esac cat >>confdefs.h <<_ACEOF #ifndef __cplusplus #define inline $ac_val #endif _ACEOF ;; esac ac_fn_c_find_intX_t "$LINENO" "32" "ac_cv_c_int32_t" case $ac_cv_c_int32_t in #( no|yes) ;; #( *) cat >>confdefs.h <<_ACEOF #define int32_t $ac_cv_c_int32_t _ACEOF ;; esac ac_fn_c_find_intX_t "$LINENO" "64" "ac_cv_c_int64_t" case $ac_cv_c_int64_t in #( no|yes) ;; #( *) cat >>confdefs.h <<_ACEOF #define int64_t $ac_cv_c_int64_t _ACEOF ;; esac ac_fn_c_check_type "$LINENO" "off_t" "ac_cv_type_off_t" "$ac_includes_default" if test "x$ac_cv_type_off_t" = xyes; then : else cat >>confdefs.h <<_ACEOF #define off_t long int _ACEOF fi ac_fn_c_check_type "$LINENO" "pid_t" "ac_cv_type_pid_t" "$ac_includes_default" if test "x$ac_cv_type_pid_t" = xyes; then : else cat >>confdefs.h <<_ACEOF #define pid_t int _ACEOF fi ac_fn_c_check_type "$LINENO" "size_t" "ac_cv_type_size_t" "$ac_includes_default" if test "x$ac_cv_type_size_t" = xyes; then : else cat >>confdefs.h <<_ACEOF #define size_t unsigned int _ACEOF fi ac_fn_c_check_type "$LINENO" "ssize_t" "ac_cv_type_ssize_t" "$ac_includes_default" if test "x$ac_cv_type_ssize_t" = xyes; then : else cat >>confdefs.h <<_ACEOF #define ssize_t int _ACEOF fi ac_fn_c_check_member "$LINENO" "struct stat" "st_blocks" "ac_cv_member_struct_stat_st_blocks" "$ac_includes_default" if test "x$ac_cv_member_struct_stat_st_blocks" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STRUCT_STAT_ST_BLOCKS 1 _ACEOF $as_echo "#define HAVE_ST_BLOCKS 1" >>confdefs.h else case " $LIBOBJS " in *" fileblocks.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS fileblocks.$ac_objext" ;; esac fi ac_fn_c_find_uintX_t "$LINENO" "16" "ac_cv_c_uint16_t" case $ac_cv_c_uint16_t in #( no|yes) ;; #( *) cat >>confdefs.h <<_ACEOF #define uint16_t $ac_cv_c_uint16_t _ACEOF ;; esac ac_fn_c_find_uintX_t "$LINENO" "32" "ac_cv_c_uint32_t" case $ac_cv_c_uint32_t in #( no|yes) ;; #( *) $as_echo "#define _UINT32_T 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define uint32_t $ac_cv_c_uint32_t _ACEOF ;; esac ac_fn_c_find_uintX_t "$LINENO" "64" "ac_cv_c_uint64_t" case $ac_cv_c_uint64_t in #( no|yes) ;; #( *) $as_echo "#define _UINT64_T 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define uint64_t $ac_cv_c_uint64_t _ACEOF ;; esac ac_fn_c_find_uintX_t "$LINENO" "8" "ac_cv_c_uint8_t" case $ac_cv_c_uint8_t in #( no|yes) ;; #( *) $as_echo "#define _UINT8_T 1" >>confdefs.h cat >>confdefs.h <<_ACEOF #define uint8_t $ac_cv_c_uint8_t _ACEOF ;; esac # Checks for library functions. { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether lstat correctly handles trailing slash" >&5 $as_echo_n "checking whether lstat correctly handles trailing slash... " >&6; } if ${ac_cv_func_lstat_dereferences_slashed_symlink+:} false; then : $as_echo_n "(cached) " >&6 else rm -f conftest.sym conftest.file echo >conftest.file if test "$as_ln_s" = "ln -s" && ln -s conftest.file conftest.sym; then if test "$cross_compiling" = yes; then : ac_cv_func_lstat_dereferences_slashed_symlink=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ $ac_includes_default int main () { struct stat sbuf; /* Linux will dereference the symlink and fail, as required by POSIX. That is better in the sense that it means we will not have to compile and use the lstat wrapper. */ return lstat ("conftest.sym/", &sbuf) == 0; ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_lstat_dereferences_slashed_symlink=yes else ac_cv_func_lstat_dereferences_slashed_symlink=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi else # If the `ln -s' command failed, then we probably don't even # have an lstat function. ac_cv_func_lstat_dereferences_slashed_symlink=no fi rm -f conftest.sym conftest.file fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_lstat_dereferences_slashed_symlink" >&5 $as_echo "$ac_cv_func_lstat_dereferences_slashed_symlink" >&6; } test $ac_cv_func_lstat_dereferences_slashed_symlink = yes && cat >>confdefs.h <<_ACEOF #define LSTAT_FOLLOWS_SLASHED_SYMLINK 1 _ACEOF if test "x$ac_cv_func_lstat_dereferences_slashed_symlink" = xno; then case " $LIBOBJS " in *" lstat.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS lstat.$ac_objext" ;; esac fi for ac_header in stdlib.h do : ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" if test "x$ac_cv_header_stdlib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STDLIB_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible malloc" >&5 $as_echo_n "checking for GNU libc compatible malloc... " >&6; } if ${ac_cv_func_malloc_0_nonnull+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : ac_cv_func_malloc_0_nonnull=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined STDC_HEADERS || defined HAVE_STDLIB_H # include #else char *malloc (); #endif int main () { return ! malloc (0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_malloc_0_nonnull=yes else ac_cv_func_malloc_0_nonnull=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_malloc_0_nonnull" >&5 $as_echo "$ac_cv_func_malloc_0_nonnull" >&6; } if test $ac_cv_func_malloc_0_nonnull = yes; then : $as_echo "#define HAVE_MALLOC 1" >>confdefs.h else $as_echo "#define HAVE_MALLOC 0" >>confdefs.h case " $LIBOBJS " in *" malloc.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS malloc.$ac_objext" ;; esac $as_echo "#define malloc rpl_malloc" >>confdefs.h fi for ac_header in stdlib.h do : ac_fn_c_check_header_mongrel "$LINENO" "stdlib.h" "ac_cv_header_stdlib_h" "$ac_includes_default" if test "x$ac_cv_header_stdlib_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_STDLIB_H 1 _ACEOF fi done { $as_echo "$as_me:${as_lineno-$LINENO}: checking for GNU libc compatible realloc" >&5 $as_echo_n "checking for GNU libc compatible realloc... " >&6; } if ${ac_cv_func_realloc_0_nonnull+:} false; then : $as_echo_n "(cached) " >&6 else if test "$cross_compiling" = yes; then : ac_cv_func_realloc_0_nonnull=no else cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if defined STDC_HEADERS || defined HAVE_STDLIB_H # include #else char *realloc (); #endif int main () { return ! realloc (0, 0); ; return 0; } _ACEOF if ac_fn_c_try_run "$LINENO"; then : ac_cv_func_realloc_0_nonnull=yes else ac_cv_func_realloc_0_nonnull=no fi rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext \ conftest.$ac_objext conftest.beam conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_func_realloc_0_nonnull" >&5 $as_echo "$ac_cv_func_realloc_0_nonnull" >&6; } if test $ac_cv_func_realloc_0_nonnull = yes; then : $as_echo "#define HAVE_REALLOC 1" >>confdefs.h else $as_echo "#define HAVE_REALLOC 0" >>confdefs.h case " $LIBOBJS " in *" realloc.$ac_objext "* ) ;; *) LIBOBJS="$LIBOBJS realloc.$ac_objext" ;; esac $as_echo "#define realloc rpl_realloc" >>confdefs.h fi for ac_func in ftruncate memset realpath socket strcasecmp strchr strncasecmp strspn strtol strtoull do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # check compatibility # Check whether --enable-largefile was given. if test "${enable_largefile+set}" = set; then : enableval=$enable_largefile; fi if test "$enable_largefile" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for special C compiler options needed for large files" >&5 $as_echo_n "checking for special C compiler options needed for large files... " >&6; } if ${ac_cv_sys_largefile_CC+:} false; then : $as_echo_n "(cached) " >&6 else ac_cv_sys_largefile_CC=no if test "$GCC" != yes; then ac_save_CC=$CC while :; do # IRIX 6.2 and later do not support large files by default, # so use the C compiler's -n32 option if that helps. cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : break fi rm -f core conftest.err conftest.$ac_objext CC="$CC -n32" if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_largefile_CC=' -n32'; break fi rm -f core conftest.err conftest.$ac_objext break done CC=$ac_save_CC rm -f conftest.$ac_ext fi fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_largefile_CC" >&5 $as_echo "$ac_cv_sys_largefile_CC" >&6; } if test "$ac_cv_sys_largefile_CC" != no; then CC=$CC$ac_cv_sys_largefile_CC fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _FILE_OFFSET_BITS value needed for large files" >&5 $as_echo_n "checking for _FILE_OFFSET_BITS value needed for large files... " >&6; } if ${ac_cv_sys_file_offset_bits+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _FILE_OFFSET_BITS 64 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_file_offset_bits=64; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_file_offset_bits=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_file_offset_bits" >&5 $as_echo "$ac_cv_sys_file_offset_bits" >&6; } case $ac_cv_sys_file_offset_bits in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _FILE_OFFSET_BITS $ac_cv_sys_file_offset_bits _ACEOF ;; esac rm -rf conftest* if test $ac_cv_sys_file_offset_bits = unknown; then { $as_echo "$as_me:${as_lineno-$LINENO}: checking for _LARGE_FILES value needed for large files" >&5 $as_echo_n "checking for _LARGE_FILES value needed for large files... " >&6; } if ${ac_cv_sys_large_files+:} false; then : $as_echo_n "(cached) " >&6 else while :; do cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=no; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #define _LARGE_FILES 1 #include /* Check that off_t can represent 2**63 - 1 correctly. We can't simply define LARGE_OFF_T to be 9223372036854775807, since some C++ compilers masquerading as C compilers incorrectly reject 9223372036854775807. */ #define LARGE_OFF_T (((off_t) 1 << 62) - 1 + ((off_t) 1 << 62)) int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 && LARGE_OFF_T % 2147483647 == 1) ? 1 : -1]; int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : ac_cv_sys_large_files=1; break fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext ac_cv_sys_large_files=unknown break done fi { $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_sys_large_files" >&5 $as_echo "$ac_cv_sys_large_files" >&6; } case $ac_cv_sys_large_files in #( no | unknown) ;; *) cat >>confdefs.h <<_ACEOF #define _LARGE_FILES $ac_cv_sys_large_files _ACEOF ;; esac rm -rf conftest* fi fi for ac_header in aio.h sched.h uuid.h sys/disk.h sys/disklabel.h do : as_ac_Header=`$as_echo "ac_cv_header_$ac_header" | $as_tr_sh` ac_fn_c_check_header_mongrel "$LINENO" "$ac_header" "$as_ac_Header" "$ac_includes_default" if eval test \"x\$"$as_ac_Header"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_header" | $as_tr_cpp` 1 _ACEOF fi done for ac_header in pthread.h do : ac_fn_c_check_header_mongrel "$LINENO" "pthread.h" "ac_cv_header_pthread_h" "$ac_includes_default" if test "x$ac_cv_header_pthread_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PTHREAD_H 1 _ACEOF fi done for ac_header in pthread_np.h do : ac_fn_c_check_header_compile "$LINENO" "pthread_np.h" "ac_cv_header_pthread_np_h" "#if HAVE_PTHREAD_H #include #endif " if test "x$ac_cv_header_pthread_np_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_PTHREAD_NP_H 1 _ACEOF fi done for ac_func in strlcpy arc4random srandomdev pthread_yield sched_yield do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done for ac_func in pthread_set_name_np setproctitle do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" if eval test \"x\$"$as_ac_var"\" = x"yes"; then : cat >>confdefs.h <<_ACEOF #define `$as_echo "HAVE_$ac_func" | $as_tr_cpp` 1 _ACEOF fi done # Extract the first word of ""mkdep"", so it can be a program name with args. set dummy "mkdep"; ac_word=$2 { $as_echo "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 $as_echo_n "checking for $ac_word... " >&6; } if ${ac_cv_path_MKDEP+:} false; then : $as_echo_n "(cached) " >&6 else case $MKDEP in [\\/]* | ?:[\\/]*) ac_cv_path_MKDEP="$MKDEP" # Let the user override the test with a path. ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. for ac_exec_ext in '' $ac_executable_extensions; do if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then ac_cv_path_MKDEP="$as_dir/$ac_word$ac_exec_ext" $as_echo "$as_me:${as_lineno-$LINENO}: found $as_dir/$ac_word$ac_exec_ext" >&5 break 2 fi done done IFS=$as_save_IFS ;; esac fi MKDEP=$ac_cv_path_MKDEP if test -n "$MKDEP"; then { $as_echo "$as_me:${as_lineno-$LINENO}: result: $MKDEP" >&5 $as_echo "$MKDEP" >&6; } else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi #if test -z "$MKDEP"; then # AC_PATH_PROG(MKDEP, "makedepend") #fi # paths configdir="$ac_default_prefix/etc/istgt" mediadir="/var/istgt" if test "$prefix" != "NONE"; then configdir="$prefix/etc/istgt" fi # Check whether --with-configdir was given. if test "${with_configdir+set}" = set; then : withval=$with_configdir; case $withval in yes|no) ;; *) configdir="$withval" ;; esac fi # Check whether --with-mediadir was given. if test "${with_mediadir+set}" = set; then : withval=$with_mediadir; case $withval in yes|no) ;; *) mediadir="$withval" ;; esac fi # test for -Wall saved_CFLAGS="$CFLAGS" { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether the C compiler accepts -Wall" >&5 $as_echo_n "checking whether the C compiler accepts -Wall... " >&6; } CFLAGS="$CFLAGS -Wall" cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ int main () { ; return 0; } _ACEOF if ac_fn_c_try_compile "$LINENO"; then : { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; }; saved_CFLAGS="$CFLAGS" else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext CFLAGS="$saved_CFLAGS" # check enable features { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use debug mode" >&5 $as_echo_n "checking whether to use debug mode... " >&6; } # Check whether --enable-debug was given. if test "${enable_debug+set}" = set; then : enableval=$enable_debug; { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 $as_echo "yes" >&6; }; $as_echo "#define DEBUG 1" >>confdefs.h else { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 $as_echo "no" >&6; } fi { $as_echo "$as_me:${as_lineno-$LINENO}: checking which syslog facility to use" >&5 $as_echo_n "checking which syslog facility to use... " >&6; } # Check whether --with-logfacility was given. if test "${with_logfacility+set}" = set; then : withval=$with_logfacility; case $with_logfacility in daemon|auth|authpriv|local0|local1|local2|local3|local4|local5|local6|local7) logfacility=$with_logfacility ;; *) as_fn_error $? "\"unsupported facility $with_logfacility\"" "$LINENO" 5 ;; esac else logfacility="local7" fi cat >>confdefs.h <<_ACEOF #define DEFAULT_LOG_FACILITY "$logfacility" _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"$logfacility\"" >&5 $as_echo "\"$logfacility\"" >&6; } { $as_echo "$as_me:${as_lineno-$LINENO}: checking which syslog priority to use" >&5 $as_echo_n "checking which syslog priority to use... " >&6; } # Check whether --with-logpriority was given. if test "${with_logpriority+set}" = set; then : withval=$with_logpriority; case $with_logpriority in emerg|alert|crit|err|warning|notice|info|debug) logpriority=$with_logpriority ;; *) as_fn_error $? "\"unsupported priority $with_logpriority\"" "$LINENO" 5 ;; esac else logpriority="info" fi cat >>confdefs.h <<_ACEOF #define DEFAULT_LOG_PRIORITY "$logpriority" _ACEOF { $as_echo "$as_me:${as_lineno-$LINENO}: result: \"$logpriority\"" >&5 $as_echo "\"$logpriority\"" >&6; } #AC_MSG_CHECKING([whether to use asynchronous io]) #AC_ARG_WITH(aio, # [ --with-aio use aio (default "no")], # [case $with_aio in # yes) # AC_DEFINE(USE_AIO, 1, [use aio]) # AC_MSG_RESULT(yes) # ;; # *) # AC_MSG_RESULT(no) # ;; # esac], # AC_MSG_RESULT(no)) #AC_SUBST(USE_AIO) for ac_header in sys/types.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/types.h" "ac_cv_header_sys_types_h" "$ac_includes_default" if test "x$ac_cv_header_sys_types_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_TYPES_H 1 _ACEOF fi done for ac_header in machine/atomic.h do : ac_fn_c_check_header_compile "$LINENO" "machine/atomic.h" "ac_cv_header_machine_atomic_h" "#if HAVE_SYS_TYPES_H #include #endif " if test "x$ac_cv_header_machine_atomic_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_MACHINE_ATOMIC_H 1 _ACEOF fi done for ac_header in sys/atomic.h do : ac_fn_c_check_header_mongrel "$LINENO" "sys/atomic.h" "ac_cv_header_sys_atomic_h" "$ac_includes_default" if test "x$ac_cv_header_sys_atomic_h" = xyes; then : cat >>confdefs.h <<_ACEOF #define HAVE_SYS_ATOMIC_H 1 _ACEOF fi done # for FeeeBSD #AC_CHECK_FUNCS([atomic_store_rel_int atomic_load_acq_int]) #AC_CHECK_FUNCS([atomic_cmpset_ptr atomic_cmpset_64 atomic_cmpset_32]) # for NetBSD #AC_CHECK_FUNCS([atomic_swap_uint atomic_or_uint_nv]) #AC_CHECK_FUNCS([atomic_cas_ptr atomic_cas_64 atomic_cas_32]) atomic="no" { $as_echo "$as_me:${as_lineno-$LINENO}: checking for atomic operations" >&5 $as_echo_n "checking for atomic operations... " >&6; } cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if HAVE_SYS_TYPES_H #include #endif #include int main () { int x, y; atomic_store_rel_int(&x, 1); y = atomic_load_acq_int(&x); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : $as_echo "#define USE_ATOMIC 1" >>confdefs.h $as_echo "#define HAVE_ATOMIC_STORE_REL_INT 1" >>confdefs.h $as_echo "#define HAVE_ATOMIC_LOAD_ACQ_INT 1" >>confdefs.h atomic="yes" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext if test $atomic = "no"; then cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if HAVE_SYS_TYPES_H #include #endif #include int main () { unsigned int x, y; atomic_swap_uint(&x, 1); y = atomic_or_uint_nv(&x, 0); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : $as_echo "#define USE_ATOMIC 1" >>confdefs.h $as_echo "#define HAVE_ATOMIC_SWAP_UINT 1" >>confdefs.h $as_echo "#define HAVE_ATOMIC_OR_UINT_NV 1" >>confdefs.h atomic="yes" fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext fi cat confdefs.h - <<_ACEOF >conftest.$ac_ext /* end confdefs.h. */ #if HAVE_SYS_TYPES_H #include #endif #include int main () { membar_producer(); ; return 0; } _ACEOF if ac_fn_c_try_link "$LINENO"; then : $as_echo "#define HAVE_MEMBAR_PRODUCER 1" >>confdefs.h fi rm -f core conftest.err conftest.$ac_objext \ conftest$ac_exeext conftest.$ac_ext { $as_echo "$as_me:${as_lineno-$LINENO}: result: $atomic" >&5 $as_echo "$atomic" >&6; } ac_config_files="$ac_config_files Makefile src/Makefile etc/Makefile doc/Makefile" ac_config_files="$ac_config_files src/build.h" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure # tests run on this system so they can be shared between configure # scripts and configure runs, see configure's option --config-cache. # It is not useful on other systems. If it contains results you don't # want to keep, you may remove or edit it. # # config.status only pays attention to the cache file if you give it # the --recheck option to rerun configure. # # `ac_cv_env_foo' variables (set or unset) will be overridden when # loading this file, other *unset* `ac_cv_foo' will be assigned the # following values. _ACEOF # The following way of writing the cache mishandles newlines in values, # but we know of no workaround that is simple, portable, and efficient. # So, we kill variables containing newlines. # Ultrix sh set writes to stderr and can't be redirected directly, # and sets the high bit in the cache file unless we assign to the vars. ( for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do eval ac_val=\$$ac_var case $ac_val in #( *${as_nl}*) case $ac_var in #( *_cv_*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 $as_echo "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; esac case $ac_var in #( _ | IFS | as_nl) ;; #( BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( *) { eval $ac_var=; unset $ac_var;} ;; esac ;; esac done (set) 2>&1 | case $as_nl`(ac_space=' '; set) 2>&1` in #( *${as_nl}ac_space=\ *) # `set' does not quote correctly, so add quotes: double-quote # substitution turns \\\\ into \\, and sed turns \\ into \. sed -n \ "s/'/'\\\\''/g; s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" ;; #( *) # `set' quotes correctly as required by POSIX, so do not add quotes. sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" ;; esac | sort ) | sed ' /^ac_cv_env_/b end t clear :clear s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/ t end s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ :end' >>confcache if diff "$cache_file" confcache >/dev/null 2>&1; then :; else if test -w "$cache_file"; then if test "x$cache_file" != "x/dev/null"; then { $as_echo "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 $as_echo "$as_me: updating cache $cache_file" >&6;} if test ! -f "$cache_file" || test -h "$cache_file"; then cat confcache >"$cache_file" else case $cache_file in #( */* | ?:*) mv -f confcache "$cache_file"$$ && mv -f "$cache_file"$$ "$cache_file" ;; #( *) mv -f confcache "$cache_file" ;; esac fi fi else { $as_echo "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 $as_echo "$as_me: not updating unwritable cache $cache_file" >&6;} fi fi rm -f confcache test "x$prefix" = xNONE && prefix=$ac_default_prefix # Let make expand exec_prefix. test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' DEFS=-DHAVE_CONFIG_H ac_libobjs= ac_ltlibobjs= U= for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue # 1. Remove the extension, and $U if already installed. ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' ac_i=`$as_echo "$ac_i" | sed "$ac_script"` # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR # will be set to the directory where LIBOBJS objects are built. as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' done LIBOBJS=$ac_libobjs LTLIBOBJS=$ac_ltlibobjs : "${CONFIG_STATUS=./config.status}" ac_write_fail=0 ac_clean_files_save=$ac_clean_files ac_clean_files="$ac_clean_files $CONFIG_STATUS" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 $as_echo "$as_me: creating $CONFIG_STATUS" >&6;} as_write_fail=0 cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 #! $SHELL # Generated by $as_me. # Run this file to recreate the current configuration. # Compiler output produced by configure, useful for debugging # configure, is in config.log if it exists. debug=false ac_cs_recheck=false ac_cs_silent=false SHELL=\${CONFIG_SHELL-$SHELL} export SHELL _ASEOF cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 ## -------------------- ## ## M4sh Initialization. ## ## -------------------- ## # Be more Bourne compatible DUALCASE=1; export DUALCASE # for MKS sh if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then : emulate sh NULLCMD=: # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which # is contrary to our usage. Disable this feature. alias -g '${1+"$@"}'='"$@"' setopt NO_GLOB_SUBST else case `(set -o) 2>/dev/null` in #( *posix*) : set -o posix ;; #( *) : ;; esac fi as_nl=' ' export as_nl # Printing a long string crashes Solaris 7 /usr/bin/printf. as_echo='\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\' as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo as_echo=$as_echo$as_echo$as_echo$as_echo$as_echo$as_echo # Prefer a ksh shell builtin over an external printf program on Solaris, # but without wasting forks for bash or zsh. if test -z "$BASH_VERSION$ZSH_VERSION" \ && (test "X`print -r -- $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='print -r --' as_echo_n='print -rn --' elif (test "X`printf %s $as_echo`" = "X$as_echo") 2>/dev/null; then as_echo='printf %s\n' as_echo_n='printf %s' else if test "X`(/usr/ucb/echo -n -n $as_echo) 2>/dev/null`" = "X-n $as_echo"; then as_echo_body='eval /usr/ucb/echo -n "$1$as_nl"' as_echo_n='/usr/ucb/echo -n' else as_echo_body='eval expr "X$1" : "X\\(.*\\)"' as_echo_n_body='eval arg=$1; case $arg in #( *"$as_nl"*) expr "X$arg" : "X\\(.*\\)$as_nl"; arg=`expr "X$arg" : ".*$as_nl\\(.*\\)"`;; esac; expr "X$arg" : "X\\(.*\\)" | tr -d "$as_nl" ' export as_echo_n_body as_echo_n='sh -c $as_echo_n_body as_echo' fi export as_echo_body as_echo='sh -c $as_echo_body as_echo' fi # The user is always right. if test "${PATH_SEPARATOR+set}" != set; then PATH_SEPARATOR=: (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || PATH_SEPARATOR=';' } fi # IFS # We need space, tab and new line, in precisely that order. Quoting is # there to prevent editors from complaining about space-tab. # (If _AS_PATH_WALK were called with IFS unset, it would disable word # splitting by setting IFS to empty value.) IFS=" "" $as_nl" # Find who we are. Look in the path if we contain no directory separator. as_myself= case $0 in #(( *[\\/]* ) as_myself=$0 ;; *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR for as_dir in $PATH do IFS=$as_save_IFS test -z "$as_dir" && as_dir=. test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break done IFS=$as_save_IFS ;; esac # We did not find ourselves, most probably we were run as `sh COMMAND' # in which case we are not to be found in the path. if test "x$as_myself" = x; then as_myself=$0 fi if test ! -f "$as_myself"; then $as_echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 exit 1 fi # Unset variables that we do not need and which cause bugs (e.g. in # pre-3.0 UWIN ksh). But do not cause bugs in bash 2.01; the "|| exit 1" # suppresses any "Segmentation fault" message there. '((' could # trigger a bug in pdksh 5.2.14. for as_var in BASH_ENV ENV MAIL MAILPATH do eval test x\${$as_var+set} = xset \ && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : done PS1='$ ' PS2='> ' PS4='+ ' # NLS nuisances. LC_ALL=C export LC_ALL LANGUAGE=C export LANGUAGE # CDPATH. (unset CDPATH) >/dev/null 2>&1 && unset CDPATH # as_fn_error STATUS ERROR [LINENO LOG_FD] # ---------------------------------------- # Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are # provided, also output the error to LOG_FD, referencing LINENO. Then exit the # script with STATUS, using 1 if that was 0. as_fn_error () { as_status=$1; test $as_status -eq 0 && as_status=1 if test "$4"; then as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack $as_echo "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 fi $as_echo "$as_me: error: $2" >&2 as_fn_exit $as_status } # as_fn_error # as_fn_set_status STATUS # ----------------------- # Set $? to STATUS, without forking. as_fn_set_status () { return $1 } # as_fn_set_status # as_fn_exit STATUS # ----------------- # Exit the shell with STATUS, even in a "trap 0" or "set -e" context. as_fn_exit () { set +e as_fn_set_status $1 exit $1 } # as_fn_exit # as_fn_unset VAR # --------------- # Portably unset VAR. as_fn_unset () { { eval $1=; unset $1;} } as_unset=as_fn_unset # as_fn_append VAR VALUE # ---------------------- # Append the text in VALUE to the end of the definition contained in VAR. Take # advantage of any shell optimizations that allow amortized linear growth over # repeated appends, instead of the typical quadratic growth present in naive # implementations. if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null; then : eval 'as_fn_append () { eval $1+=\$2 }' else as_fn_append () { eval $1=\$$1\$2 } fi # as_fn_append # as_fn_arith ARG... # ------------------ # Perform arithmetic evaluation on the ARGs, and store the result in the # global $as_val. Take advantage of shells that can avoid forks. The arguments # must be portable across $(()) and expr. if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null; then : eval 'as_fn_arith () { as_val=$(( $* )) }' else as_fn_arith () { as_val=`expr "$@" || test $? -eq 1` } fi # as_fn_arith if expr a : '\(a\)' >/dev/null 2>&1 && test "X`expr 00001 : '.*\(...\)'`" = X001; then as_expr=expr else as_expr=false fi if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then as_basename=basename else as_basename=false fi if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then as_dirname=dirname else as_dirname=false fi as_me=`$as_basename -- "$0" || $as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ X"$0" : 'X\(//\)$' \| \ X"$0" : 'X\(/\)' \| . 2>/dev/null || $as_echo X/"$0" | sed '/^.*\/\([^/][^/]*\)\/*$/{ s//\1/ q } /^X\/\(\/\/\)$/{ s//\1/ q } /^X\/\(\/\).*/{ s//\1/ q } s/.*/./; q'` # Avoid depending upon Character Ranges. as_cr_letters='abcdefghijklmnopqrstuvwxyz' as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' as_cr_Letters=$as_cr_letters$as_cr_LETTERS as_cr_digits='0123456789' as_cr_alnum=$as_cr_Letters$as_cr_digits ECHO_C= ECHO_N= ECHO_T= case `echo -n x` in #((((( -n*) case `echo 'xy\c'` in *c*) ECHO_T=' ';; # ECHO_T is single tab character. xy) ECHO_C='\c';; *) echo `echo ksh88 bug on AIX 6.1` > /dev/null ECHO_T=' ';; esac;; *) ECHO_N='-n';; esac rm -f conf$$ conf$$.exe conf$$.file if test -d conf$$.dir; then rm -f conf$$.dir/conf$$.file else rm -f conf$$.dir mkdir conf$$.dir 2>/dev/null fi if (echo >conf$$.file) 2>/dev/null; then if ln -s conf$$.file conf$$ 2>/dev/null; then as_ln_s='ln -s' # ... but there are two gotchas: # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. # In both cases, we have to default to `cp -p'. ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || as_ln_s='cp -p' elif ln conf$$.file conf$$ 2>/dev/null; then as_ln_s=ln else as_ln_s='cp -p' fi else as_ln_s='cp -p' fi rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file rmdir conf$$.dir 2>/dev/null # as_fn_mkdir_p # ------------- # Create "$as_dir" as a directory, including parents if necessary. as_fn_mkdir_p () { case $as_dir in #( -*) as_dir=./$as_dir;; esac test -d "$as_dir" || eval $as_mkdir_p || { as_dirs= while :; do case $as_dir in #( *\'*) as_qdir=`$as_echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( *) as_qdir=$as_dir;; esac as_dirs="'$as_qdir' $as_dirs" as_dir=`$as_dirname -- "$as_dir" || $as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$as_dir" : 'X\(//\)[^/]' \| \ X"$as_dir" : 'X\(//\)$' \| \ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$as_dir" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` test -d "$as_dir" && break done test -z "$as_dirs" || eval "mkdir $as_dirs" } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" } # as_fn_mkdir_p if mkdir -p . 2>/dev/null; then as_mkdir_p='mkdir -p "$as_dir"' else test -d ./-p && rmdir ./-p as_mkdir_p=false fi if test -x / >/dev/null 2>&1; then as_test_x='test -x' else if ls -dL / >/dev/null 2>&1; then as_ls_L_option=L else as_ls_L_option= fi as_test_x=' eval sh -c '\'' if test -d "$1"; then test -d "$1/."; else case $1 in #( -*)set "./$1";; esac; case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in #(( ???[sx]*):;;*)false;;esac;fi '\'' sh ' fi as_executable_p=$as_test_x # Sed expression to map a string onto a valid CPP name. as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" # Sed expression to map a string onto a valid variable name. as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" exec 6>&1 ## ----------------------------------- ## ## Main body of $CONFIG_STATUS script. ## ## ----------------------------------- ## _ASEOF test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Save the log message, to keep $0 and so on meaningful, and to # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log=" This file was extended by istgt $as_me 0.4, which was generated by GNU Autoconf 2.68. Invocation command line was CONFIG_FILES = $CONFIG_FILES CONFIG_HEADERS = $CONFIG_HEADERS CONFIG_LINKS = $CONFIG_LINKS CONFIG_COMMANDS = $CONFIG_COMMANDS $ $0 $@ on `(hostname || uname -n) 2>/dev/null | sed 1q` " _ACEOF case $ac_config_files in *" "*) set x $ac_config_files; shift; ac_config_files=$*;; esac case $ac_config_headers in *" "*) set x $ac_config_headers; shift; ac_config_headers=$*;; esac cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 # Files that config.status was made for. config_files="$ac_config_files" config_headers="$ac_config_headers" _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 ac_cs_usage="\ \`$as_me' instantiates files and other configuration actions from templates according to the current configuration. Unless the files and actions are specified as TAGs, all are instantiated by default. Usage: $0 [OPTION]... [TAG]... -h, --help print this help, then exit -V, --version print version number and configuration settings, then exit --config print configuration, then exit -q, --quiet, --silent do not print progress messages -d, --debug don't remove temporary files --recheck update $as_me by reconfiguring in the same conditions --file=FILE[:TEMPLATE] instantiate the configuration file FILE --header=FILE[:TEMPLATE] instantiate the configuration header FILE Configuration files: $config_files Configuration headers: $config_headers Report bugs to the package provider." _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config="`$as_echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`" ac_cs_version="\\ istgt config.status 0.4 configured by $0, generated by GNU Autoconf 2.68, with options \\"\$ac_cs_config\\" Copyright (C) 2010 Free Software Foundation, Inc. This config.status script is free software; the Free Software Foundation gives unlimited permission to copy, distribute and modify it." ac_pwd='$ac_pwd' srcdir='$srcdir' INSTALL='$INSTALL' test -n "\$AWK" || AWK=awk _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # The default lists apply if the user does not specify any file. ac_need_defaults=: while test $# != 0 do case $1 in --*=?*) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` ac_shift=: ;; --*=) ac_option=`expr "X$1" : 'X\([^=]*\)='` ac_optarg= ac_shift=: ;; *) ac_option=$1 ac_optarg=$2 ac_shift=shift ;; esac case $ac_option in # Handling of the options. -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) ac_cs_recheck=: ;; --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) $as_echo "$ac_cs_version"; exit ;; --config | --confi | --conf | --con | --co | --c ) $as_echo "$ac_cs_config"; exit ;; --debug | --debu | --deb | --de | --d | -d ) debug=: ;; --file | --fil | --fi | --f ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; '') as_fn_error $? "missing file argument" ;; esac as_fn_append CONFIG_FILES " '$ac_optarg'" ac_need_defaults=false;; --header | --heade | --head | --hea ) $ac_shift case $ac_optarg in *\'*) ac_optarg=`$as_echo "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; esac as_fn_append CONFIG_HEADERS " '$ac_optarg'" ac_need_defaults=false;; --he | --h) # Conflict between --help and --header as_fn_error $? "ambiguous option: \`$1' Try \`$0 --help' for more information.";; --help | --hel | -h ) $as_echo "$ac_cs_usage"; exit ;; -q | -quiet | --quiet | --quie | --qui | --qu | --q \ | -silent | --silent | --silen | --sile | --sil | --si | --s) ac_cs_silent=: ;; # This is an error. -*) as_fn_error $? "unrecognized option: \`$1' Try \`$0 --help' for more information." ;; *) as_fn_append ac_config_targets " $1" ac_need_defaults=false ;; esac shift done ac_configure_extra_args= if $ac_cs_silent; then exec 6>/dev/null ac_configure_extra_args="$ac_configure_extra_args --silent" fi _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 if \$ac_cs_recheck; then set X '$SHELL' '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion shift \$as_echo "running CONFIG_SHELL=$SHELL \$*" >&6 CONFIG_SHELL='$SHELL' export CONFIG_SHELL exec "\$@" fi _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 exec 5>>config.log { echo sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX ## Running $as_me. ## _ASBOX $as_echo "$ac_log" } >&5 _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # Handling of arguments. for ac_config_target in $ac_config_targets do case $ac_config_target in "src/config.h") CONFIG_HEADERS="$CONFIG_HEADERS src/config.h" ;; "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; "src/Makefile") CONFIG_FILES="$CONFIG_FILES src/Makefile" ;; "etc/Makefile") CONFIG_FILES="$CONFIG_FILES etc/Makefile" ;; "doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;; "src/build.h") CONFIG_FILES="$CONFIG_FILES src/build.h" ;; *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; esac done # If the user did not use the arguments to specify the items to instantiate, # then the envvar interface is used. Set only those that are not. # We use the long form for the default assignment because of an extremely # bizarre bug on SunOS 4.1.3. if $ac_need_defaults; then test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files test "${CONFIG_HEADERS+set}" = set || CONFIG_HEADERS=$config_headers fi # Have a temporary directory for convenience. Make it in the build tree # simply because there is no reason against having it here, and in addition, # creating and moving files from /tmp can sometimes cause problems. # Hook for its removal unless debugging. # Note that there is a small window in which the directory will not be cleaned: # after its creation but before its name has been assigned to `$tmp'. $debug || { tmp= ac_tmp= trap 'exit_status=$? : "${ac_tmp:=$tmp}" { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status ' 0 trap 'as_fn_exit 1' 1 2 13 15 } # Create a (secure) tmp directory for tmp files. { tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && test -d "$tmp" } || { tmp=./conf$$-$RANDOM (umask 077 && mkdir "$tmp") } || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 ac_tmp=$tmp # Set up the scripts for CONFIG_FILES section. # No need to generate them if there are no CONFIG_FILES. # This happens for instance with `./config.status config.h'. if test -n "$CONFIG_FILES"; then ac_cr=`echo X | tr X '\015'` # On cygwin, bash can eat \r inside `` if the user requested igncr. # But we know of no other shell where ac_cr would be empty at this # point, so we can use a bashism as a fallback. if test "x$ac_cr" = x; then eval ac_cr=\$\'\\r\' fi ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then ac_cs_awk_cr='\\r' else ac_cs_awk_cr=$ac_cr fi echo 'BEGIN {' >"$ac_tmp/subs1.awk" && _ACEOF { echo "cat >conf$$subs.awk <<_ACEOF" && echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && echo "_ACEOF" } >conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` ac_delim='%!_!# ' for ac_last_try in false false false false false :; do . ./conf$$subs.sh || as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` if test $ac_delim_n = $ac_delim_num; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done rm -f conf$$subs.sh cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && _ACEOF sed -n ' h s/^/S["/; s/!.*/"]=/ p g s/^[^!]*!// :repl t repl s/'"$ac_delim"'$// t delim :nl h s/\(.\{148\}\)..*/\1/ t more1 s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ p n b repl :more1 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t nl :delim h s/\(.\{148\}\)..*/\1/ t more2 s/["\\]/\\&/g; s/^/"/; s/$/"/ p b :more2 s/["\\]/\\&/g; s/^/"/; s/$/"\\/ p g s/.\{148\}// t delim ' >$CONFIG_STATUS || ac_write_fail=1 rm -f conf$$subs.awk cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 _ACAWK cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && for (key in S) S_is_set[key] = 1 FS = "" } { line = $ 0 nfields = split(line, field, "@") substed = 0 len = length(field[1]) for (i = 2; i < nfields; i++) { key = field[i] keylen = length(key) if (S_is_set[key]) { value = S[key] line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) len += length(value) + length(field[++i]) substed = 1 } else len += 1 + keylen } print line } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" else cat fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 _ACEOF # VPATH may cause trouble with some makes, so we remove sole $(srcdir), # ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and # trailing colons and then remove the whole line if VPATH becomes empty # (actually we leave an empty line to preserve line numbers). if test "x$srcdir" = x.; then ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ h s/// s/^/:/ s/[ ]*$/:/ s/:\$(srcdir):/:/g s/:\${srcdir}:/:/g s/:@srcdir@:/:/g s/^:*// s/:*$// x s/\(=[ ]*\).*/\1/ G s/\n// s/^[^=]*=[ ]*$// }' fi cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 fi # test -n "$CONFIG_FILES" # Set up the scripts for CONFIG_HEADERS section. # No need to generate them if there are no CONFIG_HEADERS. # This happens for instance with `./config.status Makefile'. if test -n "$CONFIG_HEADERS"; then cat >"$ac_tmp/defines.awk" <<\_ACAWK || BEGIN { _ACEOF # Transform confdefs.h into an awk script `defines.awk', embedded as # here-document in config.status, that substitutes the proper values into # config.h.in to produce config.h. # Create a delimiter string that does not exist in confdefs.h, to ease # handling of long lines. ac_delim='%!_!# ' for ac_last_try in false false :; do ac_tt=`sed -n "/$ac_delim/p" confdefs.h` if test -z "$ac_tt"; then break elif $ac_last_try; then as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 else ac_delim="$ac_delim!$ac_delim _$ac_delim!! " fi done # For the awk script, D is an array of macro values keyed by name, # likewise P contains macro parameters if any. Preserve backslash # newline sequences. ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* sed -n ' s/.\{148\}/&'"$ac_delim"'/g t rset :rset s/^[ ]*#[ ]*define[ ][ ]*/ / t def d :def s/\\$// t bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3"/p s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p d :bsnl s/["\\]/\\&/g s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ D["\1"]=" \3\\\\\\n"\\/p t cont s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p t cont d :cont n s/.\{148\}/&'"$ac_delim"'/g t clear :clear s/\\$// t bsnlc s/["\\]/\\&/g; s/^/"/; s/$/"/p d :bsnlc s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p b cont ' >$CONFIG_STATUS || ac_write_fail=1 cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 for (key in D) D_is_set[key] = 1 FS = "" } /^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { line = \$ 0 split(line, arg, " ") if (arg[1] == "#") { defundef = arg[2] mac1 = arg[3] } else { defundef = substr(arg[1], 2) mac1 = arg[2] } split(mac1, mac2, "(") #) macro = mac2[1] prefix = substr(line, 1, index(line, defundef) - 1) if (D_is_set[macro]) { # Preserve the white space surrounding the "#". print prefix "define", macro P[macro] D[macro] next } else { # Replace #undef with comments. This is necessary, for example, # in the case of _POSIX_SOURCE, which is predefined and required # on some systems where configure will not decide to define it. if (defundef == "undef") { print "/*", prefix defundef, macro, "*/" next } } } { print } _ACAWK _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 fi # test -n "$CONFIG_HEADERS" eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " shift for ac_tag do case $ac_tag in :[FHLC]) ac_mode=$ac_tag; continue;; esac case $ac_mode$ac_tag in :[FHL]*:*);; :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; :[FH]-) ac_tag=-:-;; :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; esac ac_save_IFS=$IFS IFS=: set x $ac_tag IFS=$ac_save_IFS shift ac_file=$1 shift case $ac_mode in :L) ac_source=$1;; :[FH]) ac_file_inputs= for ac_f do case $ac_f in -) ac_f="$ac_tmp/stdin";; *) # Look for the file first in the build tree, then in the source tree # (if the path is not absolute). The absolute path cannot be DOS-style, # because $ac_f cannot contain `:'. test -f "$ac_f" || case $ac_f in [\\/$]*) false;; *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; esac || as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; esac case $ac_f in *\'*) ac_f=`$as_echo "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac as_fn_append ac_file_inputs " '$ac_f'" done # Let's still pretend it is `configure' which instantiates (i.e., don't # use $as_me), people would be surprised to read: # /* config.h. Generated by config.status. */ configure_input='Generated from '` $as_echo "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' `' by configure.' if test x"$ac_file" != x-; then configure_input="$ac_file. $configure_input" { $as_echo "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 $as_echo "$as_me: creating $ac_file" >&6;} fi # Neutralize special characters interpreted by sed in replacement strings. case $configure_input in #( *\&* | *\|* | *\\* ) ac_sed_conf_input=`$as_echo "$configure_input" | sed 's/[\\\\&|]/\\\\&/g'`;; #( *) ac_sed_conf_input=$configure_input;; esac case $ac_tag in *:-:* | *:-) cat >"$ac_tmp/stdin" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; esac ;; esac ac_dir=`$as_dirname -- "$ac_file" || $as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$ac_file" : 'X\(//\)[^/]' \| \ X"$ac_file" : 'X\(//\)$' \| \ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || $as_echo X"$ac_file" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q'` as_dir="$ac_dir"; as_fn_mkdir_p ac_builddir=. case "$ac_dir" in .) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_dir_suffix=/`$as_echo "$ac_dir" | sed 's|^\.[\\/]||'` # A ".." for each directory in $ac_dir_suffix. ac_top_builddir_sub=`$as_echo "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` case $ac_top_builddir_sub in "") ac_top_builddir_sub=. ac_top_build_prefix= ;; *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; esac ;; esac ac_abs_top_builddir=$ac_pwd ac_abs_builddir=$ac_pwd$ac_dir_suffix # for backward compatibility: ac_top_builddir=$ac_top_build_prefix case $srcdir in .) # We are building in place. ac_srcdir=. ac_top_srcdir=$ac_top_builddir_sub ac_abs_top_srcdir=$ac_pwd ;; [\\/]* | ?:[\\/]* ) # Absolute name. ac_srcdir=$srcdir$ac_dir_suffix; ac_top_srcdir=$srcdir ac_abs_top_srcdir=$srcdir ;; *) # Relative name. ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix ac_top_srcdir=$ac_top_build_prefix$srcdir ac_abs_top_srcdir=$ac_pwd/$srcdir ;; esac ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix case $ac_mode in :F) # # CONFIG_FILE # case $INSTALL in [\\/$]* | ?:[\\/]* ) ac_INSTALL=$INSTALL ;; *) ac_INSTALL=$ac_top_build_prefix$INSTALL ;; esac _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 # If the template does not know about datarootdir, expand it. # FIXME: This hack should be removed a few years after 2.60. ac_datarootdir_hack=; ac_datarootdir_seen= ac_sed_dataroot=' /datarootdir/ { p q } /@datadir@/p /@docdir@/p /@infodir@/p /@localedir@/p /@mandir@/p' case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in *datarootdir*) ac_datarootdir_seen=yes;; *@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 $as_echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} _ACEOF cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_datarootdir_hack=' s&@datadir@&$datadir&g s&@docdir@&$docdir&g s&@infodir@&$infodir&g s&@localedir@&$localedir&g s&@mandir@&$mandir&g s&\\\${datarootdir}&$datarootdir&g' ;; esac _ACEOF # Neutralize VPATH when `$srcdir' = `.'. # Shell code in configure.ac might set extrasub. # FIXME: do we really want to maintain this feature? cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_sed_extra="$ac_vpsub $extrasub _ACEOF cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 :t /@[a-zA-Z_][a-zA-Z_0-9]*@/!b s|@configure_input@|$ac_sed_conf_input|;t t s&@top_builddir@&$ac_top_builddir_sub&;t t s&@top_build_prefix@&$ac_top_build_prefix&;t t s&@srcdir@&$ac_srcdir&;t t s&@abs_srcdir@&$ac_abs_srcdir&;t t s&@top_srcdir@&$ac_top_srcdir&;t t s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t s&@builddir@&$ac_builddir&;t t s&@abs_builddir@&$ac_abs_builddir&;t t s&@abs_top_builddir@&$ac_abs_top_builddir&;t t s&@INSTALL@&$ac_INSTALL&;t t $ac_datarootdir_hack " eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ "$ac_tmp/out"`; test -z "$ac_out"; } && { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&5 $as_echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' which seems to be undefined. Please make sure it is defined" >&2;} rm -f "$ac_tmp/stdin" case $ac_file in -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; esac \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; :H) # # CONFIG_HEADER # if test x"$ac_file" != x-; then { $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" } >"$ac_tmp/config.h" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then { $as_echo "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 $as_echo "$as_me: $ac_file is unchanged" >&6;} else rm -f "$ac_file" mv "$ac_tmp/config.h" "$ac_file" \ || as_fn_error $? "could not create $ac_file" "$LINENO" 5 fi else $as_echo "/* $configure_input */" \ && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ || as_fn_error $? "could not create -" "$LINENO" 5 fi ;; esac done # for ac_tag as_fn_exit 0 _ACEOF ac_clean_files=$ac_clean_files_save test $ac_write_fail = 0 || as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 # configure is writing to config.log, and then calls config.status. # config.status does its own redirection, appending to config.log. # Unfortunately, on DOS this fails, as config.log is still kept open # by configure, so config.status won't be able to write to it; its # output is simply discarded. So we exec the FD to /dev/null, # effectively closing config.log, so it can be properly (re)opened and # appended to by config.status. When coming back to configure, we # need to make the FD available again. if test "$no_create" != yes; then ac_cs_success=: ac_config_status_args= test "$silent" = yes && ac_config_status_args="$ac_config_status_args --quiet" exec 5>/dev/null $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false exec 5>>config.log # Use ||, not &&, to avoid exiting from the if with $? = 1, which # would make configure fail if this is the last instruction. $ac_cs_success || as_fn_exit 1 fi if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 $as_echo "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} fi istgt-20111008/doc/000755 000000 000000 00000000000 11643630133 013746 5ustar00rootwheel000000 000000 istgt-20111008/COPYRIGHT000644 000000 000000 00000002424 11507713041 014475 0ustar00rootwheel000000 000000 Copyright (C) 2008-2011 Daisuke Aoyama . All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. istgt-20111008/README000644 000000 000000 00000011674 11635327750 014103 0ustar00rootwheel000000 000000 The iSCSI target for FreeBSD 7.1 or later This software (istgt) is the implementation of iSCSI target (refer to RFC3720 about iSCSI) for FreeBSD 7.1 or later. It includes iSCSI protocol processor and SPC-3 (SCSI Primary Commands - 3) based logical unit emulation. You can easily create over 2TB storage for iSCSI target. If necessary, you can configure to use multipath failover or MCS failover. If you want use the command queuing, change QueueDepth key of the configuration. Because default is comment out, you only remove # from this line to make the parameter take effect. The command queuing is not yet stable. If you have any problem, comment out or specify 0 to disable it. The small QueueDepth may cause a problem. I recommend you to specify more than 32. You can use a file or any raw device as the backing store. Microsoft Windows Server 2008 or Microsoft iSCSI initiator 2.08 or later is recommended. Diskless iSCSI boot is tested with Intel PRO/1000 PT Server Adapter and Windows Server 2003/2008/2008R2/Windows7 RC. iSCSI install/boot is tested with gPXE 1.0.0 and WS2K8/2K8R2/W7. Intel Gigabit CT Desktop Adapter is tested with WS 2K8R2/W7. This software is under development and less document. The format of the configuration file may frequently be changed. Please use it carefully. It is made based on the concept of my own MCS extra patch (release 20090125) for iscsi-target (netbsd-iscsi-20080207). If you want to run FreeBSD in Hyper-V environment, please use my small kernel patch of reboot issue which can be downloaded below. The latest version is available at: http://shell.peach.ne.jp/aoyama/ For FreeBSD initiator, please check here: ftp://ftp.cs.huji.ac.il/users/danny/freebsd/ Using path-through tape drive from Microsoft iSCSI Initiator: open registry of initiator by regedit.exe. change the value of MaxTransferLength to 0x10000 (65536). reboot Windows. (0000 is an instance number. It might be a different value.) HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Class\ {4D36E97B-E325-11CE-BFC1-08002BE10318}\0000\Parameters\ MaxTransferLength Timeout of Microsoft iSCSI Initiator: If you want fast recovery from failure, try to decrease the value in above registry: LinkDownTime TCPConnectTime TCPDisconnectTime DelayBetweenReconnect If you want to keep failed command, try to increase: MaxRequestHoldTime SrbTimeoutDelta External Libs: o OpenSSL (shipped with FreeBSD) o CAM (shipped with FreeBSD) Tested Initiators: o Microsoft Windows Server 2008 (builtin) o Microsoft Windows Server 2008 R2 (builtin) o Microsoft Windows 7 Ultimate x64 (builtin) o Microsoft iSCSI Initiator 2.08 on WS2003 o Intel iSCSI Remote Boot 2.1.22 o Intel iSCSI Remote Boot 2.3.81 o Intel iSCSI Remote Boot 2.5.12 (CT Desktop Adapter) o Intel iSCSI Remote Boot 2.7.44 (ET Dual Port Server Adapter) o Sun VirtualBOX 2.1.2 (builtin) o Sun VirtualBOX 3.0.12 (builtin) o Sun VirtualBOX 3.1.2 (builtin) o VMware ESXi 3.5 Update 3 (builtin) o VMware ESXi 4.0 (builtin) o VMware ESXi 4.1 Update 1 (builtin) o VMware ESXi 5.0 (builtin) o Xen Server 5.0 Update 3 (builtin) o Xen Server 5.5 (builtin) o FreeBSD 7.1 + iscsi 2.1.3 o FreeBSD 7.2 + iscsi 2.2.3 o FreeBSD 7.3 + iscsi 2.2.4 o FreeBSD 8.2 + isboot 0.2.3 o CentOS 5.2 + Open-iSCSI (package) o Ubuntu 9.10 + Open-iSCSI (package) o gPXE 1.0.0 + VirtualBox 3.1.2 on FreeNAS 0.7.1 o FreeNAS 0.7.1 (FreeBSD 7.2/7.3 + iscsi 2.2.4) o FreeNAS 0.7.2 (FreeBSD 7.3 + iscsi 2.2.4) Features: o MCS/MPIO for failover (up to 255 concurrent sessions) o Multipath I/O (only support Microsoft MPIO/VMware ESXi) o SPC-3 Persistent Reservation for cluster nodes o 64bit LBA for over 2TB o Header/Data digest by CRC32C o CHAP w/Mutual authentication o Multiple LUNs and ACLs for portals o IPv6/IPv4 dual support (experimental features) o Command Queuing (up to 255 depths, disabled by default) o iSCSI boot with Intel PRO/1000 Server Adapters o virtual DVD-ROM and virtual tape drive (DLT emulator) o pass-through device (only support DA/SA/CD/CH type) Current Limitations: o logical unit is sharing with exclusive lock o exclusive port w/asynchronous operation o command queuing is incomplete for multiple initiators o LUN1 and more are shared information of LUN0 (e.g. UnitInquiry) o APTPL (Activate Persist Through Power Loss) is not supported o TPGS is incomplete for multipath failover (disabled by default) o path-through SA variable length must not exceed 65536 (CAM limit) o maximum LUN is 15 o maximum PortalGroup are 1024 o maximum InitiatorGroup, LogicalUnit are 4096 o maximum mapping per target is 256 o maximum concurrent session identifier per target is 255 o maximum persistent reservation keys per target is 256 (most case of limitation is due to memory allocation and finding method by linear search) Special Thanks to: Isao Takatani (bug report and idea suggestion) Toshiya Shimpo of Meikohsha Information System Co.,Ltd Yoshio Takii of Meikohsha Information System Co.,Ltd Author: Daisuke Aoyama istgt-20111008/src/000755 000000 000000 00000000000 11643630133 013770 5ustar00rootwheel000000 000000 istgt-20111008/Makefile.in000644 000000 000000 00000004311 11635327750 015256 0ustar00rootwheel000000 000000 # top Makefile top_srcdir = @top_srcdir@ srcdir = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ datadir = @datarootdir@ libexecdir = @libexecdir@ PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ document = COPYRIGHT README INSTALL ChangeLog.jp VER_H = src/istgt_ver.h DISTBASE = istgt DISTVER = `sed -e '/ISTGT_VERSION/!d' -e 's/[^0-9.]*\([0-9.a-z]*\).*/\1/' $(VER_H)` DISTEXTVER = `sed -e '/ISTGT_EXTRA_VERSION/!d' -e 's/[^0-9.]*\([0-9.a-z]*\).*/\1/' $(VER_H)` #DISTDIR = $(PACKAGE_NAME)-$(PACKAGE_VERSION) #DISTDIR = $(DISTBASE)-$(DISTVER)-$(DISTEXTVER) DISTDIR = $(DISTBASE)-$(DISTEXTVER) DISTNAME = $(DISTDIR).tar.gz DISTFILES = Makefile.in configure.in install-sh configure \ $(header) $(source) $(ctl_header) $(ctl_source) \ $(document) $(sample) SUBDIRS = src etc doc ######################################################################### all: for subdir in $(SUBDIRS); do \ (cd $$subdir; $(MAKE) $@) || exit $$?; \ done install: for subdir in $(SUBDIRS); do \ (cd $$subdir; $(MAKE) $@) || exit $$?; \ done install-doc: for subdir in doc; do \ (cd $$subdir; $(MAKE) $@) || exit $$?; \ done dist: distdir rm -rf $(DISTDIR) $(DISTNAME) mkdir $(DISTDIR) for file in $(DISTFILES); do \ cp -p $(srcdir)/$$file $(DISTDIR); \ done for subdir in $(SUBDIRS); do \ (cd $$subdir; $(MAKE) subdir=$$subdir $@) || exit $$?; \ done tar cf - $(DISTDIR) | gzip -9c > $(DISTNAME) rm -rf $(DISTDIR) distdir distdir: echo $(DISTDIR) >$@ clean: local-clean for subdir in $(SUBDIRS); do \ (cd $$subdir; $(MAKE) $@) || exit $$?; \ done distclean: clean local-distclean for subdir in $(SUBDIRS); do \ (cd $$subdir; $(MAKE) $@) || exit $$?; \ done local-clean: -rm -f a.out *.o *.core -rm -f *~ local-distclean: local-clean -rm -f Makefile config.status config.cache config.log config.h -rm -f $(DISTNAME) distdir depend: for subdir in $(SUBDIRS); do \ (cd $$subdir; $(MAKE) $@) || exit $$?; \ done ######################################################################### istgt-20111008/etc/000755 000000 000000 00000000000 11643630133 013754 5ustar00rootwheel000000 000000 istgt-20111008/install-sh000755 000000 000000 00000032464 11161466525 015225 0ustar00rootwheel000000 000000 #!/bin/sh # install - install a program, script, or datafile scriptversion=2006-12-25.00 # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the # following copyright and license. # # Copyright (C) 1994 X Consortium # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to # deal in the Software without restriction, including without limitation the # rights to use, copy, modify, merge, publish, distribute, sublicense, and/or # sell copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN # AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- # TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # # Except as contained in this notice, the name of the X Consortium shall not # be used in advertising or otherwise to promote the sale, use or other deal- # ings in this Software without prior written authorization from the X Consor- # tium. # # # FSF changes to this file are in the public domain. # # Calling this script install-sh is preferred over install.sh, to prevent # `make' implicit rules from creating a file called install from it # when there is no Makefile. # # This script is compatible with the BSD install script, but was written # from scratch. nl=' ' IFS=" "" $nl" # set DOITPROG to echo to test this script # Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} if test -z "$doit"; then doit_exec=exec else doit_exec=$doit fi # Put in absolute file names if you don't have them in your path; # or use environment vars. chgrpprog=${CHGRPPROG-chgrp} chmodprog=${CHMODPROG-chmod} chownprog=${CHOWNPROG-chown} cmpprog=${CMPPROG-cmp} cpprog=${CPPROG-cp} mkdirprog=${MKDIRPROG-mkdir} mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} posix_glob='?' initialize_posix_glob=' test "$posix_glob" != "?" || { if (set -f) 2>/dev/null; then posix_glob= else posix_glob=: fi } ' posix_mkdir= # Desired mode of installed file. mode=0755 chgrpcmd= chmodcmd=$chmodprog chowncmd= mvcmd=$mvprog rmcmd="$rmprog -f" stripcmd= src= dst= dir_arg= dst_arg= copy_on_change=false no_target_directory= usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE or: $0 [OPTION]... SRCFILES... DIRECTORY or: $0 [OPTION]... -t DIRECTORY SRCFILES... or: $0 [OPTION]... -d DIRECTORIES... In the 1st form, copy SRCFILE to DSTFILE. In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. In the 4th, create DIRECTORIES. Options: --help display this help and exit. --version display version info and exit. -c (ignored) -C install only if different (preserve the last data modification time) -d create directories instead of installing files. -g GROUP $chgrpprog installed files to GROUP. -m MODE $chmodprog installed files to MODE. -o USER $chownprog installed files to USER. -s $stripprog installed files. -t DIRECTORY install into DIRECTORY. -T report an error if DSTFILE is a directory. Environment variables override the default commands: CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG RMPROG STRIPPROG " while test $# -ne 0; do case $1 in -c) ;; -C) copy_on_change=true;; -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 case $mode in *' '* | *' '* | *' '* | *'*'* | *'?'* | *'['*) echo "$0: invalid mode: $mode" >&2 exit 1;; esac shift;; -o) chowncmd="$chownprog $2" shift;; -s) stripcmd=$stripprog;; -t) dst_arg=$2 shift;; -T) no_target_directory=true;; --version) echo "$0 $scriptversion"; exit $?;; --) shift break;; -*) echo "$0: invalid option: $1" >&2 exit 1;; *) break;; esac shift done if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. # Otherwise, the last argument is the destination. Remove it from $@. for arg do if test -n "$dst_arg"; then # $@ is not empty: it contains at least $arg. set fnord "$@" "$dst_arg" shift # fnord fi shift # arg dst_arg=$arg done fi if test $# -eq 0; then if test -z "$dir_arg"; then echo "$0: no input file specified." >&2 exit 1 fi # It's OK to call `install-sh -d' without argument. # This can happen when creating conditional directories. exit 0 fi if test -z "$dir_arg"; then trap '(exit $?); exit' 1 2 13 15 # Set umask so as not to create temps with too-generous modes. # However, 'strip' requires both read and write access to temps. case $mode in # Optimize common cases. *644) cp_umask=133;; *755) cp_umask=22;; *[0-7]) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then u_plus_rw= else u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac fi for src do # Protect names starting with `-'. case $src in -*) src=./$src;; esac if test -n "$dir_arg"; then dst=$src dstdir=$dst test -d "$dstdir" dstdir_status=$? else # Waiting for this to be detected by the "$cpprog $src $dsttmp" command # might cause directories to be created, which would be especially bad # if $src (and thus $dsttmp) contains '*'. if test ! -f "$src" && test ! -d "$src"; then echo "$0: $src does not exist." >&2 exit 1 fi if test -z "$dst_arg"; then echo "$0: no destination specified." >&2 exit 1 fi dst=$dst_arg # Protect names starting with `-'. case $dst in -*) dst=./$dst;; esac # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then if test -n "$no_target_directory"; then echo "$0: $dst_arg: Is a directory" >&2 exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else # Prefer dirname, but fall back on a substitute if dirname fails. dstdir=` (dirname "$dst") 2>/dev/null || expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ X"$dst" : 'X\(//\)[^/]' \| \ X"$dst" : 'X\(//\)$' \| \ X"$dst" : 'X\(/\)' \| . 2>/dev/null || echo X"$dst" | sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ s//\1/ q } /^X\(\/\/\)[^/].*/{ s//\1/ q } /^X\(\/\/\)$/{ s//\1/ q } /^X\(\/\).*/{ s//\1/ q } s/.*/./; q' ` test -d "$dstdir" dstdir_status=$? fi fi obsolete_mkdir_used=false if test $dstdir_status != 0; then case $posix_mkdir in '') # Create intermediate dirs using mode 755 as modified by the umask. # This is like FreeBSD 'install' as of 1997-10-28. umask=`umask` case $stripcmd.$umask in # Optimize common cases. *[2367][2367]) mkdir_umask=$umask;; .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; *[0-7]) mkdir_umask=`expr $umask + 22 \ - $umask % 100 % 40 + $umask % 20 \ - $umask % 10 % 4 + $umask % 2 `;; *) mkdir_umask=$umask,go-w;; esac # With -d, create the new directory with the user-specified mode. # Otherwise, rely on $mkdir_umask. if test -n "$dir_arg"; then mkdir_mode=-m$mode else mkdir_mode= fi posix_mkdir=false case $umask in *[123567][0-7][0-7]) # POSIX mkdir -p sets u+wx bits regardless of umask, which # is incompatible with FreeBSD 'install' when (umask & 300) != 0. ;; *) tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 if (umask $mkdir_umask && exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 then if test -z "$dir_arg" || { # Check for POSIX incompatibilities with -m. # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or # other-writeable bit of parent directory when it shouldn't. # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. ls_ld_tmpdir=`ls -ld "$tmpdir"` case $ls_ld_tmpdir in d????-?r-*) different_mode=700;; d????-?--*) different_mode=755;; *) false;; esac && $mkdirprog -m$different_mode -p -- "$tmpdir" && { ls_ld_tmpdir_1=`ls -ld "$tmpdir"` test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" } } then posix_mkdir=: fi rmdir "$tmpdir/d" "$tmpdir" else # Remove any dirs left behind by ancient mkdir implementations. rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null fi trap '' 0;; esac;; esac if $posix_mkdir && ( umask $mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else # The umask is ridiculous, or mkdir does not conform to POSIX, # or it failed possibly due to a race condition. Create the # directory the slow way, step by step, checking for races as we go. case $dstdir in /*) prefix='/';; -*) prefix='./';; *) prefix='';; esac eval "$initialize_posix_glob" oIFS=$IFS IFS=/ $posix_glob set -f set fnord $dstdir shift $posix_glob set +f IFS=$oIFS prefixes= for d do test -z "$d" && continue prefix=$prefix$d if test -d "$prefix"; then prefixes= else if $posix_mkdir; then (umask=$mkdir_umask && $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break # Don't fail if two instances are running concurrently. test -d "$prefix" || exit 1 else case $prefix in *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; *) qprefix=$prefix;; esac prefixes="$prefixes '$qprefix'" fi fi prefix=$prefix/ done if test -n "$prefixes"; then # Don't fail if two instances are running concurrently. (umask $mkdir_umask && eval "\$doit_exec \$mkdirprog $prefixes") || test -d "$dstdir" || exit 1 obsolete_mkdir_used=true fi fi fi if test -n "$dir_arg"; then { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 else # Make a couple of temp file names in the proper directory. dsttmp=$dstdir/_inst.$$_ rmtmp=$dstdir/_rm.$$_ # Trap to clean up those temp files at exit. trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 # Copy the file name to the temp name. (umask $cp_umask && $doit_exec $cpprog "$src" "$dsttmp") && # and set any options; do chmod last to preserve setuid bits. # # If any of these fail, we abort the whole thing. If we want to # ignore errors from any of these, just make sure not to ignore # errors from the above "$doit $cpprog $src $dsttmp" command. # { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && eval "$initialize_posix_glob" && $posix_glob set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && $posix_glob set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then rm -f "$dsttmp" else # Rename the file to the real destination. $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || # The rename failed, perhaps because mv can't rename something else # to itself, or perhaps because mv is so ancient that it does not # support -f. { # Now remove or move aside any old file at destination location. # We try this two ways since rm can't unlink itself on some # systems and the destination file might be busy for other # reasons. In this case, the final cleanup might fail but the new # file should still install successfully. { test ! -f "$dst" || $doit $rmcmd -f "$dst" 2>/dev/null || { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } } || { echo "$0: cannot unlink or rename $dst" >&2 (exit 1); exit 1 } } && # Now rename the file to the real destination. $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 trap '' 0 fi done # Local variables: # eval: (add-hook 'write-file-hooks 'time-stamp) # time-stamp-start: "scriptversion=" # time-stamp-format: "%:y-%02m-%02d.%02H" # time-stamp-end: "$" # End: istgt-20111008/ChangeLog.jp000644 000000 000000 00000072037 11643630071 015375 0ustar00rootwheel000000 000000 2011/10/08 2011/10/08版 ノーマルセッション中のSendTargetsの応答を追加 F_SETFLのエラーを出さないよう修正 2011/10/02 リードキャッシュ、ライトキャッシュのダミー設定を追加 文字列の比較をstrcasecmpに統一 ロック期間・範囲の調整 2011/09/28 2011/09/28版 NetBSDのアトミックプリミティブに対応 COMPARE AND WRITEをtraditionalで未定義に変更 動作モードにnormalを追加してデフォルトに設定 ブロック数の計算で符号つき無しを統一 2011/09/26 LUNキューの処理方法を修正 ATSの比較エラーを表示しないように変更 送信スレッドにタイムアウトを設定 結果キューの初期化・終了忘れを修正 2011/09/24 状態チェックのロックをアトミック命令に変更 2011/09/18 2011/09/18版 グループの最大数を1024から4096に変更 2011/09/13 デーモン化前に出力フラッシュを追加 WRITE_SAMEの転送済みサイズの間違いを修正 WRITE_SAMEを複数ブロックまとめて処理するように変更 作業用サイズのマクロ名を変更 Makefileで-Cを使わないように変更 (Thanks to Stephen Borrill) 2011/09/07 2011/09/07版 ターゲット名のチェックを強化 COMPARE AND WRITEを実装 EXTENDED COPYのダミー処理を追加 VPDデータを修正 2011/09/05 LUN毎の回転数(RPM)、形状(FormFactor)設定を追加 LUN毎のシリアル設定を追加(デフォルト値は id+"L"+LUN番号) WRITE_SAMEの最大LBAチェックを先に実行するよう変更 2011/09/02 2011/09/02版 WRITE_SAME(10)/WRITE_SAME(16)を実装 2011/08/31 Block Length に256KBと512KBを追加 Thin Provisioning関連のダミーを追加 Block Limits と Block Device Characteristics のVPDを追加 TargetAddress を Discovery でのみ返すように変更 AIOの処理を削除 2011/08/22 configのパスを修正 AIOが無効の場合は sigwaitinfo を使わないように差し戻し PATH_MAXがない場合を考慮するように変更 Makefileで下層の処理エラーを反映するように変更 DESTDIRを処理するように変更 (Thanks to Arno Toell) 2011/07/17 2011/07/17版 全イニシエータ拒否の指定!ALLを追加 無指定時のQueueDepthを32に変更 O_FSYNCがない場合はO_SYNCを指定するように変更 スレッドスタックサイズを2MBに変更 2011/07/03 イニシエータ指定で!を先頭につけると拒否する機能を追加 初期ファイルサイズを最少サイズで割り当てるように変更(-m1) ファイルの範囲外で全てゼロの場合は書き込みスキップに変更(-m1) ロックおよび初期化位置を修正 AIOを使った書き込み処理を追加(デフォルトOFF) Discoveryセッションで処理するべきでないキーを追加 2011/05/29 2011/05/29版 マルチセッションの時にキューが不足する問題を修正 R2Tの処理にmutexを追加 ダミーの送信ソケット追加 送信スレッドに強制中断チェックを追加 初期定義ファイルで InitiatorName "ALL" を設定 起動スクリプトに reload を追加 (ports/156117) 2011/02/23 2011/02/23版 特定環境でメモリリークする問題を修正 2011/02/19 2011/02/19版 全スレッドにスレッド属性を適用するように修正 2011/02/18 10Gbpsで期待通りにパフォーマンスが伸びない問題を修正 スレッドロックを改良 2011/01/14 プロトコルスレッドのスタックサイズを2MBから4MBに変更 スレッドロックを改良 2011/01/03 2011/01/03版 0.4系として独立 ISTGT_DISABLE_SENDER_THREADの設定を削除 動作モードを変更するスイッチを追加(デフォルトは20100707互換) ダイジェスト機能を有効にしていると接続できない問題を修正 2011/01/02 2011/01/02版 シーケンシャル書き込みの結果を送信スレッドに渡すように変更 ISTGT_DISABLE_SENDER_THREADをistgt_iscsi.cで定義する事で 送信スレッドの作成を行わない従来モードになるように追加 MaxOutstandingR2Tの設定より多いR2Tを先送りしないように変更 メモリ解放をキュー実行直後にするように変更 2011/01/01 シーケンシャル書き込みが送信スレッドにブロックされないよう修正 2010/12/30 2010/12/30版 SCSI応答のループ外でロックをするように変更 各PDU毎のメモリ割り当て回数を削減 DATA-OUTを使わない応答は送信スレッドに直接渡すように変更 PDU送信スレッドを追加 デフォルトのウィンドウサイズを8に変更 PDU書き込み中に分断されないようにロック取得するように修正 接続情報のtypoを修正 (Thanks to Kevin Day) 2010/07/07 2010/07/07版 DefaultTime2Waitの比較を最大値を取るように修正 ターゲットが存在しない場合に検索した記録を残すように修正 デバッグ用のトレースを追加 errno比較の定数をシンボルに置換 istgt_dumpが不要な戻り値を返すのを修正 2010/06/06 2010/06/06版 NO_R2T_PRESENDの定義を廃止(設定ファイルで無効化できる為) トレースモードでない場合はPDU実行前後のロックを取らないように変更 状態ロック範囲をグローバルから各ターゲット毎に変更 2010/06/05 各ターゲット毎にローカル設定を持てるように設定を追加 2010/06/04 InitialR2T, ImmediateData, DataPDUInOrder, DataSequenceInOrder, ErrorRecoveryLevel の設定を追加 2010/06/01 LUNリセット時にファイルの再オープンをするように変更 リソース不足のエラー抜けを追加 パラメータ長の不足を修正 2010/05/27 SCSI応答のエラーにもイニシエータ及びターゲットポート表示を追加 2010/05/25 2010/05/25版 F_Bitの処理が抜けているのを修正 ショートパケットの解放間違いを修正 TL>FB+MBの条件下で追加のR2Tが送信されない問題を修正 2010/05/22 2010/05/22版 8KB, 16KB, 32KB, 64KB, 128KBの論理ブロック長を追加 認証なしを強制するオプションを追加(AuthMethod None) readキャッシュの状態をMODESENSEで返すように追加 kqueueのクリーンナップ処理を追加 configureのバージョンを更新 インデントと折り返しを修正 cmd_queueのロック忘れを修正 予約ポートの解放忘れを修正 2010/05/21 2010/05/21版 WRITEコマンドで先送りするR2T最大数の設定を追加(省略時 MaxR2T=16) 2010/05/20 エラー時にイニシエータ及びターゲットポートを表示するように修正 8KB以下のショートパケットにデータ領域を作成しないように変更 終了処理で実行中タスクの解放を行うように変更 ソケットlistenのバックログを10から2に変更 2010/05/16 2010/05/16版 MaxOutstandingR2T, DefaultTime2Wait, DefaultTime2Retain の設定を追加 R2Tの送信・処理を平行できるように修正 R2Tが必要な場合はキューイング前に送出するように変更 (istgt_iscsi.c の NO_R2T_PRESEND を定義する事により無効) ESXiのイニシエータ利用を想定したデフォルト値に変更 初期データおよびセンスデータのメモリ割り当てを先に実行するように修正 syslog priority のデフォルトを notice から info に変更 2010/05/15 0.2ブランチを作成 乱数生成に arc4random を利用するように修正 Read/Writeキャッシュの状態を保存するように修正 シグナルスレッドの終了を待つように修正 メインスレッドにスレッド名を付けるように修正 copy_pdu, srandom, arc4random 関数を外部リンケージに変更 logout関数でセッションポインタをチェックするように修正 configポインタのクリア位置を修正 2010/04/07 2010/04/07版 NetBSD, Mac OS X, Linuxなどブロック型のサイズも取得するように修正 Auto sizeエラーの場合に対象ファイルも表示するように修正 トレース中のみデバイスサイズ取得時の失敗を出すように修正 トレース中のみ重複予約に対して警告を出すように修正 /usr/local/をヘッダに埋め込まないように修正 利用していないコンフィグファイルを読まないように修正 デフォルトノード名を iqn.2007-09.jp.ne.peach.istgt に変更 デフォルトベンダ名をOS毎に指定するように修正 認証ファイルのデフォルトパスを修正 2010/02/19 エラー表示が正しくされないのを修正 2010/01/25 2010/01/25版 シリアル番号生成機能を一時無効に修正 pthread_set_name_npでスレッド名をつけるように修正 (Thanks to Ivan Voras) 2010/01/22 NodeBase名からシリアル番号を生成するように修正 実行待ちタスクをエラー中断できるように修正 クリーンナップ処理で条件待ちにabortを返すように修正 スレッド条件待ち時間の最小を5秒に修正 2010/01/12 2010/01/12版 テープとチェンジャーのタイムアウト値を修正 固定フォーマットの場合は正確にあわせるように修正 センスデータ長の間違いを修正 2010/01/11 MMCデバイスにMODESENSE(6)を発行しないように修正 パススルーでBlockDescriptorがない場合に誤動作するのを修正 istgtcontrol:setproctitle がない場合の処理を修正 2010/01/05 2010/01/05版 uctl: パススルーデバイスを出力するように修正 uctl: LUNタイプを出力するように修正 configure.inを現行ソースに合わせて修正 パススルーでMODESENSE(6)エラー時に(10)を再実行できるように修正 istgtcontrol:verboseモードを追加して既存コードを移動 istgtcontrol:プロセスタイトルにバージョンを表示するように修正 2010/01/04 MediaDirectoryの中にないメディアへの交換を拒否するように修正 設定ファイルにMediaDirectoryを追加 テープ作成前に制御ブロックが破損していないか検査するように修正 クローズ処理が必要ない場合はすぐ戻るように修正 全書き込みビットがないファイルを読み込み専用と認識するように修正 メディア自体の読み込み属性表示を追加 書き込みできないメディアファイルに読み込み専用フラグを追加 istgtcontrol: AuthMethod Autoの処理を改善 istgtcontrol: 認証ユーザとシークレットを指定可能に修正 2010/01/02 /dev/nullのメディアを空スロットと認識するように修正 メインスレッドでコネクションスレッド終了を少し待つように修正 明示して変数を初期化するように修正 COPYRIGHT表記を修正 2009/12/27 kqueue/keventを利用するように修正 ソケットタイマーを常時設定するように修正 ソケット入出力の呼び出し関数を変更 関数呼び出しを削減するように修正 2009/12/25 2009/12/25版 CAMタイムアウトを修正 パススルーでMODESENSEに失敗する場合でも通過するように修正 TargetNameがiSCSI名規則に合致するか確認するように修正 関数呼び出しを減らすようにマクロ追加など修正 FirstBurstLengthとMaxBurstLengthはコピーを持つように修正 2009/12/22 2009/12/22版 デフォルトのキューサイズを32に変更 2009/12/20 エラー時の処理抜けを修正 エラーメッセージの表示間違いを修正 syslogプライオリティをコンパイル時に指定できるように修正 高負荷時に問題を起こすので以前の処理に変更 2009/12/19 ナノ秒指定になっていなかったのを修正 待ち時間を増加、タイムアウト値と連動するように修正 タスクの処理開始時間を記録するよう修正 ベンダ定義VPDをエラーから警告に修正 Task管理でStatSNの更新をしないように修正 ポインタキャストの警告を修正 2009/12/18 2009/12/18版 syslogファシリティをコマンドラインで指定できるように追加 2009/12/16 Logoutパケットでエラーを出さないように修正 StarPortのExpStatSNエラーの回避策を追加 最大ユニット数、イニシエータ・ポータルグループ数を増加 セッションテーブルを最大ユニット数で確保するように修正 PIDの書き込み前及び終了時にpidfileを削除するように修正 Autoサイズのエラーを表示するように修正 2009/12/11 2009/12/11版 CAMエラー時の処理を上層に引き渡すように修正 CAM呼び出しのデフォルトタイムアウト値を修正 PASS型のセンスデータの応答ずれを修正 PASS型のバージョンチェックが通らないのを修正 PASS型でチェンジャーデバイスを見るように修正 PASS型で4KB境界調整を行うように修正 DVD/TAPE型でREQUEST_SENSEの応答データずれを修正 ディスク型でREQUEST_SENSEに応答するように修正 2009/12/03 セッション強制終了の前に少し待機するように修正 セッション検索時に初期値をいれるように変更 ソケット読み出しを優先するように変更 2009/11/30 2009/11/30版 ショートサイズ判定を4Kから8Kに変更 TAPE型のメディア変更が正しく扱えない問題を修正 警告ログを抑制するオプションを追加 接続リセットとタイムアウトのイニシエータを記録するように修正 DATA-OUT時にLUNフィールドをチェックしないように修正 PDU読み込み失敗時に未初期化状態になるのを修正 2009/11/24 2009/11/24版 StatSNのチェックにキューサイズを考慮するように修正 PDUエラーで接続リセットとタイムアウトを表示するように修正 PDU書き込みエラー番号を記録するように修正 書き込み処理中のR2Tの追加発行を行うように修正 NOPOUTのIビットをチェックするように修正 SCSI2互換LUN予約解放でイニシエータ名を考慮するように修正 2009/11/12 2009/11/12版 デフォルトのコンパイルフラグを修正 デフォルトのキューサイズを20に変更 エラーメッセージの変更 2009/09/08 シリアル番号演算のマクロ展開が正しく行われない問題を修正 シリアル番号に減算をしないように修正 2009/04/30 AI_NUMERICSERVがない環境の場合は0を想定するように修正 configureのチェックライブラリを修正 2009/04/29 パススルーでatapicamを考慮するように修正 2009/04/28 2009/04/28版 接続情報の取得時にマルチ接続を正しく扱えない問題を修正 シリアル番号演算を適用するように修正 キューイング数の計算を間違えていたのを修正 セッションロックの二重UNLOCKを削除 2009/04/27 2009/04/27版 キューイングによりセンスデータが破壊される問題を修正 不要になった変数を削除 2009/04/25 2009/04/25版 MCSの逆順チェックを条件変数でチェックするように修正 PDUの読み出し前にコピーフラグを消すように修正 双方向入出力のSCSIコマンドにエラーを出すように修正 DATA-OUTのエラー処理を修正 DATA-OUTが完了したら処理を切り離すように修正 実行中のタスク完了を待って終了するように修正 2009/04/24 保留したコマンドを再投入時にイベントを消去するように修正 RejectコマンドでExpCmdSNとMaxCmdSNが増加しないように修正 以前のソケットI/Oルーチンを利用するように修正 2009/04/22 2009/04/22版 TSIHを期待通りに解放していない問題を修正 readループを利用するように変更 pollの待ち時間を3000msに変更 cond_waitの待ち時間を30sに変更 MCS逆順の待ち時間を10000msに変更 DATA-OUTのLUNチェックをエラーから警告に変更 2009/04/20 2009/04/20版 ブロッキングモードのread/writeを利用するように修正 コマンドキューが有効な時はExtended INQUIRYに反映するように修正 サンプル定義ファイルのキューの深さを32に変更 FFPの移行がDiscoveryセッションであるか調べるように修正 ソケットエラーの場合はerrnoをログに記録するように修正 2009/04/16 2009/04/16版 R2TにLUNを載せていなかったのを修正 AC_SYS_LARGEFILEをテストするように修正 シンボリックリンクに対するサイズ取得は実行しないように修正 RAWデバイスに対してもAutoサイズで割り当てるように修正 既存のファイルを壊さないで作成するように修正 ターゲットの接続情報を取得するINFOコマンドを追加 istgtcontrol: INFOコマンド対応 2009/04/14 20090414版 READ(6)/WRITE(6)のLBA上位5ビットが欠落する問題を修正 2009/04/12 20090412版 データ設定取得マクロを関数呼び出しからマクロ展開コードに変更 ショートサイズの判定に誤ったマクロを利用していたのを修正 ショートサイズ送信機能を有効にするマクロ定義を追加 Sビットが有効な時にのみオバー/アンダーフローを返すように修正 Discoveryセッションは最大接続数を1に制限するように修正 NOPOUTの返答データを最小サイズに制限するように修正 DATAOUTで各種パラメータの有効性を確認するように修正 NOPIN間隔が0の場合は送信しないように修正 ソケットI/Oのリトライ位置を変更 8192バイト未満のバッファサイズは割り当てないように修正 syslogファシリティを設定ファイルで指定できるように追加 2009/03/26 20090326版 無効なLUNに対するエラーログを出さないように修正 リセット処理でLUをチェックするように修正 タスク管理コマンドでキューをクリアするように修正 2009/03/25 ワイルドカードアドレスを接続IPに変換するように修正 istgtcontrol: iqn指定がALLの場合の処理を追加 タスク数の定数をマクロ定義に変更 タスクをコネクションデータに保存するように修正 タスク実行時のエラーをチェックできるように修正 LUNライト前に保留PDUが次のCmdSNならキューに投入するように修正 エラー時のデッドロック回避を行うように修正 2009/03/23 20090323版 LUNスレッドの起動順序が正しくないのを修正 dumpデータを一行単位で一括して出力するように修正 2009/03/22 タイムアウト値を修正 2009/03/21 トレースログ関数の呼び出し前にフラグチェックするように修正 リセット処理でLUNタイプをチェックするように修正 pthread系などの戻り値チェックを間違えていたのを修正 mutexの不要なunlock呼び出しを修正 PDUデータのコピー処理を追加 終了要求されたかどうかPDU受信毎にチェックするように修正 2009/03/20 キューサイズを調整できるように設定を追加 小さいPDUはヘッダとデータ部分を一緒にwriteするように修正 保留PDUの後にエラーが発生した場合にfreeしてしまう問題を修正 2009/03/19 AHSのサイズ計算を間違えていたのを修正 MCSでCmdSNが逆順に到着した場合の待機時間を200msから1000msに変更 ロック期間を短くするように調整 iqn.以外にeui.とnaa.も完全指定を許可するように変更 2009/03/18 コネクション単位のSCSIセンス用データ領域を64Kから4Kに縮小 仮想テープで終了時のメモリサイズ指定、解放忘れを修正 コネクション単位の固定バッファからコマンド単位の固定または 動的バッファを利用するように修正 セッションデータ用のmutex_destroyを呼び出していないのを修正 LUの終了時にエラー停止する可能性があったのを修正 コネクション生成削除用ロックなどのトレースメッセージを追加 LU RESETが発生した場合に自分の接続は消滅しないように修正 SIGRTMIN+XXもしくはSIGIOによるシグナル配送に対応 2009/03/17 トレースログでログイン、ログアウトが二重に表示されるのを修正 MaxSessionsとMaxConnectionsに0が指定できた問題を修正 再接続時にMaxConnectionsを超えるコネクションが存在する場合は 強制終了を試みるように変更 キューイング処理用LUNスレッドを追加 ディスク型にコマンドキューイングする場合の処理を追加 MCSでCmdSNが逆順に到着した場合の待機時間を100msから200msに変更 2009/03/15 docディレクトリを常時作成していたのを修正 mkdepがない場合でもバイナリ作成できるように修正 2009/03/14 20090314版 同一のポータルタグ、イニシエータからMCS接続を複数した場合に セッションを間違える問題を修正 CAMライブラリが存在しない環境はパススルー処理しないように修正 NetBSDにない関数を置き換えるように追加 移植に問題を起こしそうな部分を修正 セッションロックのミスを修正 2009/03/13 20090313版 パススルーでシーケンシャルデバイスをサポートするように追加修正 トレース用メッセージの修正 CAMエラー処理が正しくないのを修正 2009/03/09 20090309版 MRDSを指定しないイニシエータを考慮するように修正 設定ファイルで指定しない場合のデフォルトの値を調整 NOPIN間隔を調整できるように追加 トレース用ダンプを標準エラー出力に変更 正常ログインもsyslogに記録するように修正 2009/03/08 20090308版 パススルーはサポート外コマンドでも渡すように修正 MRDS > FB の状態でPDUを正しく受信できない問題を修正 NOPOUTが予期しないStatSNエラーになる問題を修正 保留するコマンドキューに制限がなかった問題を修正 PROでSPEC_I_PTの扱いが適切でない問題を修正 PROでキーの登録数をチェックしていない問題を修正 ディスク型はUNIT ATTENTION(POWER ON, RESET)を発行するように追加 TargetAliasが無い時の動作が期待通りでないので修正 2009/03/07 パススルーでFirstBurstに乗らないRW以外のデータも扱えるように修正 初期化時にUNIT ATTENTIONを返した場合にリトライするように修正 初期化時に媒体容量をチェックするように追加 サポート外ユニットにINQUIRY以外発行しないように修正 パススルーでDVDROMデバイスをサポートするように追加修正 2009/03/06 20090306版 再投入するデータを戻し忘れてクラッシュする問題を修正 消失したと思われるコマンドを読み飛ばすように修正 ダイレクトアクセス型のパススルーデバイスを追加 2009/03/04 20090304版 LU初期化時にActive/optimizedを初期値とするように変更 Task Managementの処理はパケット消失に備えてCmdSN/StatSNをリセット するように修正 データ転送時のDATAOUTパケット以外を転送終了後に再投入するように修正 INQUIRYのTPGSをサポートなしに修正 2009/03/03 StatSNの管理を次に送信するべき値を保持するように修正 仮想DVDROM、仮想テープもディスク型と同様のINQUIRYに修正 マルチパスで複数のPDUを許可するように暫定処理を追加 2009/03/02 NAA形式のINQUIRYも出力するように修正 標準INQUIRYのHISUPビットをセットするように修正 アイドル時にNOPINコマンドを発行するように追加 TargetAliasが存在しない場合も送信していた問題を修正 SIGINFOを捕捉するように修正 2009/03/01 20090301版 Discoveryセッションで接続ポートからLUにアクセス可能か調べるように修正 Extension Keyをエラーログに残さないように修正 AuthMethod Noneを廃止してAutoを追加 GB表示の媒体サイズを少数第一位まで表示するように修正 2009/02/28 20090228版 パラメータチェックの前にコピーしていた問題を修正 Declarativeパラメータを送信しないように修正 NOPOUT応答時にStatSNを進めないように修正 INQUIRY VPD 0x85/0x87/0x88に応答するように追加 ロジカルユニットが無い場合はポータルタグを送信しないように修正 エミュレート応答が期待通りに動かないのを修正 2009/02/27 20090227版 イニシエータ、イニシエータポート、ターゲット、ターゲットポートを それぞれ小文字に変換した値を保持するように修正 PRO REGISTER/PREEMPT AND ABORTを実装 PRI REPORT CAPABILITIES/READ FULL STATUSを実装 Disk型のRELEASE(6)/RELEASE(10)/RESERVE(6)/RESERVE(10)を エミュレート応答に修正 2009/02/26 認証用データの解放忘れを修正 PERSISTENT RESERVE INとPERSISTENT RESERVE OUTに応答するように追加 PRI READ KEYS/READ RESERVATIONを実装 PRO RESERVE/RELEASE/PREEMPT/REGISTER AND IGNORE EXISTING KEYを実装 PRO CLEARを実装 各SCSIコマンドで影響を受ける場所にチェックポイントを追加 2009/02/24 REPORT LUNSのCDBチェックを行うように修正 接続したポータルインデックスの初期化忘れを修正 接続したポータルグループ及びロジカルユニットグループをINQUIRYに追加 REPORT TARGET PORT GROUPSに応答するように追加 接続のないLUNにアクセスした場合にクラッシュする問題を修正 ロジカルユニットの最大タグ値のチェック忘れを修正 immediateにマークされたPDUに対してCmdSNを進めてしまっていたのを修正 リセットが発生した場合はイニシエータの全コネクションに対して停止を するように修正 MODE SELECTの長さ0とヘッダに満たない特殊なケースを処理するように修正 SET TARGET PORT GROUPSに応答するように追加 2009/02/23 20090223版 Securityステージを実行しないイニシエータと接続できない問題を修正 StatSNの初期化忘れを修正 prefix等をサンプル設定及び起動スクリプトファイルに反映するように修正 関数名の変更もれを修正 2009/02/22 istgt初期リリース(20090222版) MSC Extraパッチのコンセプトを元に新規に処理ルーチンを書き直しました 複数ポータル、複数グループ、FreeBSD7.xとZFS用をコンセプトにしています intgt以前の主要な変更点 2009/01/25 20090125版 Windows7ベータ版によるディスクレスブートでの問題を修正しました ステータスシーケンス番号エラーを修正しました 2009/01/22 20090122版 仮想ディスクのロック方式を仮想DVDROMと仮想テープに合わせました 一部のiSCSIブート機能に対応しました 2009/01/17 20090117版 仮想DVDROM及び仮想TAPE(DLT8000)エミュレータを追加しました サイズの加減算表記を追加しました メディア制御機能istgtcontrolを追加しました 2008/12/25 20081225版 MODESENSEのライトキャッシュの状態をO_FSYNCに反映するようになりました シグナル処理スレッドを新設しました SIGTERM/STGINTに対して終了処理を行うように修正しました 2008/12/23 20081223版 パッチ量が多くなった為にMCSパッチからMCS Extraパッチと呼称変更しました 初期版から搭載していたMCS簡易同期機能を廃止した 管理テーブルを活用するようになった 2008/12/21 20081221版 64bitLBAに対応した Discoveryセッションにも認証を強制できるようになった 認証ファイル、PIDファイルを個別に指定できるようになった サイズサフィックスにZEPも追加した 2008/12/18 20081218版 CHAP相互認証機能を追加しました 2008/12/17 20081217版 認証、ダイジェストの強制モードを追加しました SCSIステータスを最終PDUに載せるようになりました 2008/12/11 20081211版 バグフィックス 2008/12/05 20081205版 コネクションスレッド制御用の管理テーブルを作成しました 排他ロック範囲を当該スレッドに限定するように変更しました データ及びヘッダダイジェスト機能(CRC32C)をサポートしました 2008/12/02 20081202版 マルチセッション用にmutexによる排他ロック制御を導入しました 2008/11/30 20081130版 バグフィックス 2008/11/29 20081129版 バグフィックス 2008/11/18 20081118版 初期リリース ports/net/iscsi-target(netbsd-iscsi-20080207)を元にMCSパッチを 作成しました istgt-20111008/etc/Makefile.in000644 000000 000000 00000004303 11627501424 016023 0ustar00rootwheel000000 000000 # for etc top_srcdir = @top_srcdir@ srcdir = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ datadir = @datarootdir@ libexecdir = @libexecdir@ configdir = @configdir@ mediadir = @mediadir@ SED = @SED@ source = header = document = sample = auth.conf istgtcontrol.conf \ istgt.large.conf.in istgt.conf.in istgt.sh.in DISTDIR = $(top_srcdir)/`cat $(top_srcdir)/distdir` DISTFILES = Makefile.in \ $(header) $(source) \ $(document) $(sample) ######################################################################### all: istgt.large.conf istgt.conf istgt.sh istgt.large.conf: $(SED) -e 's:%%SYSCONFDIR%%:$(sysconfdir):g' \ -e 's:%%CONFIGDIR%%:$(configdir):g' \ -e 's:%%MEDIADIR%%:$(mediadir):g' < istgt.large.conf.in > $@ istgt.conf: $(SED) -e 's:%%SYSCONFDIR%%:$(sysconfdir):g' \ -e 's:%%CONFIGDIR%%:$(configdir):g' \ -e 's:%%MEDIADIR%%:$(mediadir):g' < istgt.conf.in > $@ istgt.sh: $(SED) -e 's:%%SYSCONFDIR%%:$(sysconfdir):g' \ -e 's:%%CONFIGDIR%%:$(configdir):g' \ -e 's:%%MEDIADIR%%:$(mediadir):g' \ -e 's:%%BINDIR%%:$(bindir):g' < istgt.sh.in > $@ dist: $(DISTFILES) mkdir $(DISTDIR)/$(subdir) cd $(srcdir); thisdir=`pwd`; \ for file in $(DISTFILES); do \ cp -p $$thisdir/$$file $(DISTDIR)/$(subdir); \ done install: install-dirs install -c -m 0644 istgt.conf $(DESTDIR)$(sysconfdir)/istgt/istgt.conf.sample install -c -m 0644 istgt.large.conf \ $(DESTDIR)$(sysconfdir)/istgt/istgt.large.conf.sample install -c -m 0600 auth.conf $(DESTDIR)$(sysconfdir)/istgt/auth.conf.sample install -c -m 0600 istgtcontrol.conf \ $(DESTDIR)$(sysconfdir)/istgt/istgtcontrol.conf.sample # if [ ! -f $(sysconfdir)/rc.d/istgt ]; then \ # install -c -m 0555 istgt.sh $(DESTDIR)$(sysconfdir)/rc.d/istgt; \ # fi install -c -m 0555 istgt.sh $(DESTDIR)$(sysconfdir)/rc.d/istgt install-dirs: mkdir -p $(DESTDIR)$(sysconfdir)/istgt mkdir -p $(DESTDIR)$(sysconfdir)/rc.d clean: -rm -f istgt.large.conf istgt.conf istgt.sh -rm -f a.out *.o *.core -rm -f *~ distclean: clean -rm -f Makefile config.status config.cache config.log config.h depend: ######################################################################### istgt-20111008/etc/istgt.sh.in000644 000000 000000 00000000735 11627501424 016056 0ustar00rootwheel000000 000000 #!/bin/sh # PROVIDE: istgt # REQUIRE: NETWORKING # BEFORE: mountcritremote # KEYWORD: shutdown . /etc/rc.subr name="istgt" rcvar=`set_rcvar` extra_commands="reload" load_rc_config $name : ${istgt_enable="NO"} : ${istgt_config="%%CONFIGDIR%%/istgt.conf"} : ${istgt_pidfile="/var/run/istgt.pid"} : ${istgt_flags=""} required_files="${istgt_config}" pidfile="${istgt_pidfile}" command="%%BINDIR%%/istgt" command_args="-c ${istgt_config} ${istgt_flags}" run_rc_command "$1" istgt-20111008/etc/istgt.conf.in000644 000000 000000 00000011777 11643630071 016400 0ustar00rootwheel000000 000000 # istgt configuration file # # Please do not write parameters excluding ASCII. # The parameter must be quoted if space or separator include. # # configuration syntax: # spaces at head of line are deleted, other spaces are as separator # starting with '#' is comment line, not evaluate # ending with '\' is concatenating next line # bracket key is section key grouping following keys # group number of section key is used as tag number # (group number might not be continuous value) # 1st string is key may appear multiple times # 2nd and more are value associated the key # Comment key is placed for human readable like user interface # [Global] Comment "Global section" # node name (not include optional part) NodeBase "iqn.2007-09.jp.ne.peach.istgt" # files PidFile /var/run/istgt.pid AuthFile %%CONFIGDIR%%/auth.conf # directories # for removable media (virtual DVD/virtual Tape) MediaDirectory %%MEDIADIR%% # syslog facility LogFacility "local7" # socket I/O timeout sec. (polling is infinity) Timeout 30 # NOPIN sending interval sec. NopInInterval 20 # authentication information for discovery session DiscoveryAuthMethod Auto #DiscoveryAuthGroup AuthGroup9999 # reserved maximum connections and sessions # NOTE: iSCSI boot is 2 or more sessions required MaxSessions 16 MaxConnections 4 # maximum number of sending R2T in each connection # actual number is limited to QueueDepth and MaxCmdSN and ExpCmdSN # 0=disabled, 1-256=improves large writing MaxR2T 32 # iSCSI initial parameters negotiate with initiators # NOTE: incorrect values might crash MaxOutstandingR2T 16 DefaultTime2Wait 2 DefaultTime2Retain 60 FirstBurstLength 262144 MaxBurstLength 1048576 MaxRecvDataSegmentLength 262144 # NOTE: not supported InitialR2T Yes ImmediateData Yes DataPDUInOrder Yes DataSequenceInOrder Yes ErrorRecoveryLevel 0 [UnitControl] Comment "Internal Logical Unit Controller" #AuthMethod Auto AuthMethod CHAP Mutual AuthGroup AuthGroup10000 # this portal is only used as controller (by istgtcontrol) # if it's not necessary, no portal is valid #Portal UC1 [::1]:3261 Portal UC1 127.0.0.1:3261 # accept IP netmask #Netmask [::1] Netmask 127.0.0.1 # You should set IPs in /etc/rc.conf for physical I/F [PortalGroup1] Comment "SINGLE PORT TEST" # Portal Label(not used) IP(IPv6 or IPv4):Port #Portal DA1 [2001:03e0:06cf:0003:021b:21ff:fe04:f405]:3260 Portal DA1 192.168.2.36:3260 # wildcard address you may need if use DHCP # DO NOT USE WITH OTHER PORTALS #[PortalGroup1] # Comment "ANY IP" # # Portal Label(not used) IP(IPv6 or IPv4):Port # #Portal DA1 [::]:3260 # Portal DA1 0.0.0.0:3260 [InitiatorGroup1] Comment "Initiator Group1" # name with ! deny login/discovery #InitiatorName "!iqn.1991-05.com.microsoft:moon" # spetified name allow login/discovery #InitiatorName "iqn.1991-05.com.microsoft:saturn" # special word "ALL" match all of initiators InitiatorName "ALL" Netmask 192.168.2.0/24 # TargetName, Mapping, UnitType, LUN0 are minimum required [LogicalUnit1] Comment "Hard Disk Sample" # full specified iqn (same as below) #TargetName iqn.2007-09.jp.ne.peach.istgt:disk1 # short specified non iqn (will add NodeBase) TargetName disk1 TargetAlias "Data Disk1" # use initiators in tag1 via portals in tag1 Mapping PortalGroup1 InitiatorGroup1 # accept both CHAP and None AuthMethod Auto AuthGroup AuthGroup1 #UseDigest Header Data UseDigest Auto UnitType Disk # SCSI INQUIRY - Vendor(8) Product(16) Revision(4) Serial(16) #UnitInquiry "FreeBSD" "iSCSI Disk" "0123" "10000001" # Queuing 0=disabled, 1-255=enabled with specified depth. #QueueDepth 32 # override global setting if need #MaxOutstandingR2T 16 #DefaultTime2Wait 2 #DefaultTime2Retain 60 #FirstBurstLength 262144 #MaxBurstLength 1048576 #MaxRecvDataSegmentLength 262144 #InitialR2T Yes #ImmediateData Yes #DataPDUInOrder Yes #DataSequenceInOrder Yes #ErrorRecoveryLevel 0 # LogicalVolume for this unit on LUN0 # for file extent LUN0 Storage /tank/iscsi/istgt-disk1 10GB # for raw device extent #LUN0 Storage /dev/ad4 Auto # for ZFS volume extent #LUN0 Storage /dev/zvol/tank/istgt-vol1 Auto # override the serial of LUN0 specified with UnitInquiry #LUN0 Option Serial "10000001" # for 3.5inch, 7200rpm HDD # RPM 0=not reported, 1=non-rotating(SSD), n>1024 rpm #LUN0 Option RPM 7200 # FormFactor 0=not reported, 1=5.25, 2=3.5, 3=2.5, 4=1.8, 5=less 1.8 inch #LUN0 Option FormFactor 2 # for 2.5inch, SSD #LUN0 Option RPM 1 #LUN0 Option FormFactor 3 # for future use (enabled by default) #LUN0 Option ReadCache Disable #LUN0 Option WriteCache Disable #[LogicalUnit2] # # SCSI commands pass through to SCSI device by CAM # Comment "Pass-through Disk Sample" # TargetName pass-disk1 # TargetAlias "Pass Through Disk1" # Mapping PortalGroup1 InitiatorGroup1 # AuthMethod Auto # AuthGroup AuthGroup1 # UnitType Pass # # DO NOT SPECIFY PARTITION, PASS-THROUGH USE ENTIRE LOGICAL UNIT # LUN0 Device /dev/da0 istgt-20111008/etc/istgtcontrol.conf000644 000000 000000 00000001335 11161466525 017367 0ustar00rootwheel000000 000000 # istgtcontrol configuration file # [Global] Comment "sample configuration" # default values can be overrided by command line switch. # socket I/O timeout sec. Timeout 60 # authentication information #AuthMethod Auto AuthMethod CHAP Mutual Auth "testuser" "secret" "mutual user" "mutual secret" #AuthMethod CHAP #Auth "testuser" "secret" # default controller's host name or IP and port Host localhost #Host 192.168.2.36 Port 3261 # default target name and LUN #TargetName "iqn.xxxx" TargetName "iqn.2007-09.jp.ne.peach:dvd1" Lun 0 # removable file's flag, initial size if creating # ro: read only, rw: read and write # auto: use existing size or minimum size Flags "ro" Size "auto" istgt-20111008/etc/auth.conf000644 000000 000000 00000001632 11415046013 015561 0ustar00rootwheel000000 000000 # authentication information # # do not forget to change permission of this file # and remove sample users :-) # # currntly, this file is read at each authentication # [AuthGroup1] Comment "Auth Group1" # User Secret MutualUser MutualSecret (Mutual is optional) # chap secret length is from 12 to 16 (for MS initiator) Auth "iqn.1991-05.com.microsoft:saturn" "1234567890abcdef" \ "iqn.2007-09.jp.ne.peach:disk1" "mutualtestxx" Auth "iqn.1991-05.com.microsoft:athena" "abcdefghijklmnop" # tag 9999 is defined as AuthGroup for Dicsovery in sample istgt.conf [AuthGroup9999] Auth "iqn.1991-05.com.microsoft:saturn" "testchaptest" \ "iqn.2007-09.jp.ne.peach:disk1" "mutualtestxx" # tag 10000 is defined as AuthGroup for UnitControl in sample istgt.conf [AuthGroup10000] Comment "Unit Controller's users" Auth "ctluser" "test" "mutualuser" "mutualsecret" Auth "onlysingle" "secret" istgt-20111008/etc/istgt.large.conf.in000644 000000 000000 00000021351 11643630071 017456 0ustar00rootwheel000000 000000 # istgt configuration file # # Please do not write parameters excluding ASCII. # The parameter must be quoted if space or separator include. # # configuration syntax: # spaces at head of line are deleted, other spaces are as separator # starting with '#' is comment line, not evaluate # ending with '\' is concatenating next line # bracket key is section key grouping following keys # group number of section key is used as tag number # (group number might not be continuous value) # 1st string is key may appear multiple times # 2nd and more are value associated the key # Comment key is placed for human readable like user interface # [Global] Comment "Global section" # node name (not include optional part) NodeBase "iqn.2007-09.jp.ne.peach.istgt" # files PidFile /var/run/istgt.pid AuthFile %%CONFIGDIR%%/auth.conf # directories # for removable media (virtual DVD/virtual Tape) #MediaDirectory /mnt MediaDirectory %%MEDIADIR%% #MediaDirectory /tank/iscsi # syslog facility LogFacility "local7" # socket I/O timeout sec. (polling is infinity) Timeout 30 # NOPIN sending interval sec. NopInInterval 20 # authentication information for discovery session DiscoveryAuthMethod Auto #DiscoveryAuthMethod CHAP #DiscoveryAuthGroup AuthGroup9999 # reserved maximum connections and sessions # NOTE: iSCSI boot is 2 or more sessions required MaxSessions 16 MaxConnections 4 # maximum number of sending R2T in each connection # actual number is limited to QueueDepth and MaxCmdSN and ExpCmdSN # 0=disabled, 1-256=improves large writing MaxR2T 32 # iSCSI initial parameters negotiate with initiators # NOTE: incorrect values might crash MaxOutstandingR2T 16 DefaultTime2Wait 2 DefaultTime2Retain 60 FirstBurstLength 262144 MaxBurstLength 1048576 MaxRecvDataSegmentLength 262144 # NOTE: not supported InitialR2T Yes ImmediateData Yes DataPDUInOrder Yes DataSequenceInOrder Yes ErrorRecoveryLevel 0 [UnitControl] Comment "Internal Logical Unit Controller" #AuthMethod Auto AuthMethod CHAP Mutual AuthGroup AuthGroup10000 # this portal is only used as controller (by istgtcontrol) # if it's not necessary, no portal is valid #Portal UC1 [::1]:3261 Portal UC1 127.0.0.1:3261 #Portal UC1 192.168.2.36:3261 # accept IP netmask #Netmask [::1] Netmask 127.0.0.1 #Netmask 192.168.2.0/24 #Netmask 192.168.2.36/32 # You should set IPs in /etc/rc.conf for physical I/F [PortalGroup1] Comment "DUAL CONTROLLER TEST" # Portal Label(not used) IP(IPv6 or IPv4):Port Portal DA1 192.168.3.36:3260 Portal DA2 192.168.4.36:3260 Portal DB1 192.168.3.37:3260 Portal DB2 192.168.4.37:3260 [PortalGroup2] Comment "IPv6 TEST" Portal DA1 [2001:03e0:06cf:0003:021b:21ff:fe04:f405]:3260 Portal DA2 [2001:03e0:06cf:0004:021f:d0ff:fe16:38bd]:3260 #Portal DA3 [2001:380:e00:5:203:47ff:fe72:34f2]:3260 [PortalGroup3] Comment "SINGLE PORT TEST" Portal DA1 192.168.2.36:3260 [PortalGroup10] Comment "SINGLE PORT TEST" Portal DA1 192.168.2.37:3260 # wildcard address you may need if use DHCP # DO NOT USE WITH OTHER PORTALS #[PortalGroup4] # Comment "ANY IP" # #Portal DA1 [::]:3260 # Portal DA1 0.0.0.0:3260 [InitiatorGroup1] # initiator group1 Comment "Initiator Group1" InitiatorName "iqn.1991-05.com.microsoft:ceres" InitiatorName "iqn.1991-05.com.microsoft:saturn" Netmask 192.168.3.0/24 Netmask 192.168.4.0/24 [InitiatorGroup2] # initiator group2 Comment "Initiator Group2" InitiatorName "iqn.1991-05.com.microsoft:athena" InitiatorName "iqn.1991-05.com.microsoft:venus" InitiatorName "iqn.1991-05.com.microsoft:saturn" Netmask [2001:3e0:6cf:2::]/64 Netmask [2001:380:e00:5::]/64 [InitiatorGroup3] InitiatorName "iqn.1991-05.com.microsoft:saturn" Netmask 192.168.2.0/24 [InitiatorGroup4] Comment "ALL initiators from 192.168.2.0/24" # name with ! deny login/discovery #InitiatorName "!iqn.1991-05.com.microsoft:moon" # spetified name allow login/discovery #InitiatorName "iqn.1991-05.com.microsoft:saturn" # special word "ALL" match all of initiators InitiatorName ALL Netmask 192.168.2.0/24 [InitiatorGroup5] Comment "Initiator Group5" InitiatorName "iqn.2007-09.jp.ne.peach:pluto" Netmask 192.168.2.0/24 Netmask 192.168.3.0/24 Netmask 192.168.4.0/24 Netmask [2001:3e0:6cf:2::]/64 Netmask [2001:380:e00:5::]/64 [InitiatorGroup6] InitiatorName "iqn.2008-04.com.sun.virtualbox.initiator" Netmask 192.168.2.0/24 [InitiatorGroup256] Comment "ALL initiators from ALL IP" InitiatorName ALL Netmask ALL # TargetName, Mapping, UnitType, LUN0 are minimum required [LogicalUnit1] Comment "Hard Disk Sample" # full specified iqn (same as below) #TargetName iqn.2007-09.jp.ne.peach.istgt:disk1 # short specified non iqn (will add NodeBase) TargetName disk1 TargetAlias "Data Disk1" # use both portal group tag1 and tag3 for initiator tag1 Mapping PortalGroup3 InitiatorGroup1 Mapping PortalGroup1 InitiatorGroup1 #Mapping PortalGroup2 InitiatorGroup2 #AuthMethod CHAP Mutual AuthMethod CHAP #AuthMethod Auto AuthGroup AuthGroup1 # force use digest Header/Data/Auto UseDigest Header Data #UseDigest Header #UseDigest Data #UseDigest Auto ReadOnly No UnitType Disk UnitOnline Yes # SCSI INQUIRY - Vendor(8) Product(16) Revision(4) Serial(16) #UnitInquiry "FreeBSD" "iSCSI Disk" "0123" "10000001" # one of 512,1024,2048,4096 (default 512 is strongly recommended) #BlockLength 512 # Queuing 0=disabled, 1-255=enabled with specified depth. #QueueDepth 32 # override global setting if need #MaxOutstandingR2T 16 #DefaultTime2Wait 2 #DefaultTime2Retain 60 #FirstBurstLength 262144 #MaxBurstLength 1048576 #MaxRecvDataSegmentLength 262144 #InitialR2T Yes #ImmediateData Yes #DataPDUInOrder Yes #DataSequenceInOrder Yes #ErrorRecoveryLevel 0 # LogicalVolume for this unit on LUN0 # for file extent LUN0 Storage /tank/iscsi/istgt-disk1 10GB # for raw device extent #LUN0 Storage /dev/ad4 Auto # for ZFS volume extent #LUN0 Storage /dev/zvol/tank/istgt-vol1 Auto # override the serial of LUN0 specified with UnitInquiry #LUN0 Option Serial "10000001" # for 3.5inch, 7200rpm HDD # RPM 0=not reported, 1=non-rotating(SSD), n>1024 rpm LUN0 Option RPM 7200 # FormFactor 0=not reported, 1=5.25, 2=3.5, 3=2.5, 4=1.8, 5=less 1.8 inch LUN0 Option FormFactor 2 # for 2.5inch, SSD #LUN0 Option RPM 1 #LUN0 Option FormFactor 3 # for future use (enabled by default) #LUN0 Option ReadCache Disable #LUN0 Option WriteCache Disable #LUN1 Storage /tank/iscsi/istgt-disk1.1 10GB #LUN1 Option Serial "10000001L1" LUN2 Storage /tank/iscsi/istgt-disk1.2 10GB #LUN2 Option Serial "10000001L2" [LogicalUnit2] Comment "Hard Disk Sample" #TargetName disk2 TargetName iqn.1994-04.org.netbsd.iscsi-target:target11 TargetAlias "iSCSI Boot Disk" # use portal group tag1/2/3 for initiator tag5 Mapping PortalGroup1 InitiatorGroup5 Mapping PortalGroup2 InitiatorGroup5 Mapping PortalGroup3 InitiatorGroup5 AuthMethod Auto AuthGroup None #AuthMethod CHAP Mutual #UseDigest Header Data UseDigest Auto ReadOnly No UnitType Disk UnitOnline Yes # you can specify one of '+' or '-' value LUN0 Storage /tank/iscsi/istgt-disk2 20GB-1MB # 2PB-1MB is maximum for current windows GUI manager #LUN1 Storage /tank/iscsi/istgt-disk2.1 2PB-1MB # probably 8EB is seek limit on FreeBSD #LUN2 Storage /tank/iscsi/istgt-disk2.2 8EB-1 [LogicalUnit3] Comment "DVD ROM sample" TargetName dvd1 #TargetName iqn.1994-04.org.netbsd.iscsi-target:target44 TargetAlias "DVD ROM Drive1" Mapping PortalGroup3 InitiatorGroup4 AuthMethod Auto UseDigest Auto ReadOnly Yes UnitType DVD UnitOnline Yes #UnitInquiry "NetBSD" "iSCSI DVDROM" "0" #UnitInquiry "FreeBSD" "iSCSI DVDROM" "0001" "10000003" # auto size is depend on actual file size LUN0 Removable ro /tank/iscsi/dvds/dvd1.iso auto [LogicalUnit4] Comment "DLT sample" TargetName tape1 TargetAlias "Tape Drive1" Mapping PortalGroup3 InitiatorGroup4 AuthMethod Auto UseDigest Auto ReadOnly No UnitType Tape UnitOnline Yes #UnitInquiry "QUANTUM" "DLT8000" "C001" "CX10000001" LUN0 Removable "rw" /tank/iscsi/tapes/tape1.vt 1GB # extend file size if EOM reached #LUN1 Removable "rw,extend" /tank/iscsi/tapes/tape1.vt 1GB # file size grow and shrink automatically by EOF (ignore specified size) #LUN2 Removable "rw,dynamic" /tank/iscsi/tapes/tape1.vt auto [LogicalUnit10] Comment "Hard Disk Sample" TargetName disk3 Mapping PortalGroup3 InitiatorGroup6 UnitType Disk # currently zvol is slow due to ZIL commit LUN0 Storage /dev/zvol/tank/istgt-vol1 20GB [LogicalUnit11] Comment "Hard Disk Sample" TargetName disk4 Mapping PortalGroup10 InitiatorGroup5 UnitType Disk LUN0 Storage /tank/iscsi/istgt-disk4 16GB istgt-20111008/src/istgt.h000644 000000 000000 00000017122 11640373417 015304 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_H #define ISTGT_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef USE_ATOMIC #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_MACHINE_ATOMIC_H #include #endif #ifdef HAVE_SYS_ATOMIC_H #include #endif #endif /* USE_ATOMIC */ #include "build.h" #include #include #include "istgt_log.h" #include "istgt_conf.h" #define MAX_TMPBUF 1024 #define MAX_ADDRBUF 64 #define MAX_INITIATOR_ADDR (MAX_ADDRBUF) #define MAX_INITIATOR_NAME 256 #define MAX_TARGET_ADDR (MAX_ADDRBUF) #define MAX_TARGET_NAME 256 #define MAX_ISCSI_NAME 256 #define MAX_UCPORTAL 16 #define MAX_PORTAL 1024 #define MAX_INITIATOR 256 #define MAX_NETMASK 256 #define MAX_INITIATOR_GROUP 4096 #define MAX_LOGICAL_UNIT 4096 #define MAX_R2T 256 #define DEFAULT_CONFIG BUILD_ETC_ISTGT "/istgt.conf" #define DEFAULT_PIDFILE "/var/run/istgt.pid" #define DEFAULT_AUTHFILE BUILD_ETC_ISTGT "/auth.conf" #if 0 #define DEFAULT_MEDIAFILE BUILD_ETC_ISTGT "/media.conf" #define DEFAULT_LIVEFILE BUILD_ETC_ISTGT "/istgt.live" #endif #define DEFAULT_MEDIADIRECTORY BUILD_VAR_ISTGT #define DEFAULT_NODEBASE "iqn.2007-09.jp.ne.peach.istgt" #define DEFAULT_PORT 3260 #define DEFAULT_MAX_SESSIONS 16 #define DEFAULT_MAX_CONNECTIONS 4 #define DEFAULT_MAXOUTSTANDINGR2T 16 #define DEFAULT_DEFAULTTIME2WAIT 2 #define DEFAULT_DEFAULTTIME2RETAIN 20 #define DEFAULT_FIRSTBURSTLENGTH 65536 #define DEFAULT_MAXBURSTLENGTH 262144 #define DEFAULT_MAXRECVDATASEGMENTLENGTH 8192 #define DEFAULT_INITIALR2T 1 #define DEFAULT_IMMEDIATEDATA 1 #define DEFAULT_DATAPDUINORDER 1 #define DEFAULT_DATASEQUENCEINORDER 1 #define DEFAULT_ERRORRECOVERYLEVEL 0 #define DEFAULT_TIMEOUT 30 #define DEFAULT_NOPININTERVAL 20 #define DEFAULT_MAXR2T 16 #define ISTGT_PG_TAG_MAX 0x0000ffffU #define ISTGT_LU_TAG_MAX 0x0000ffffU #define ISTGT_UC_TAG 0x00010000U #define ISTGT_IOBUFSIZE (2 * 1024 * 1024) #define ISTGT_SNSBUFSIZE 4096 #define ISTGT_SHORTDATASIZE 8192 #define ISTGT_SHORTPDUSIZE (48+4+ISTGT_SHORTDATASIZE+4) #define ISTGT_CONDWAIT (50 * 1000) /* ms */ #define ISTGT_CONDWAIT_MIN (5 * 1000) /* ms */ #define ISTGT_STACKSIZE (2 * 1024 * 1024) #if defined (SIGRTMIN) #define ISTGT_SIGWAKEUP (SIGRTMIN + 1) #define ISTGT_USE_SIGRT #elif defined (SIGIO) #define ISTGT_SIGWAKEUP (SIGIO) #else #error "no signal for internal" #endif #if defined (__FreeBSD__) || defined (__NetBSD__) || defined (__OpenBSD__) #define ISTGT_USE_KQUEUE #endif #define MTX_LOCK(MTX) \ do { \ if (pthread_mutex_lock((MTX)) != 0) { \ ISTGT_ERRLOG("lock error\n"); \ pthread_exit(NULL); \ } \ } while (0) #define MTX_UNLOCK(MTX) \ do { \ if (pthread_mutex_unlock((MTX)) != 0) { \ ISTGT_ERRLOG("unlock error\n"); \ pthread_exit(NULL); \ } \ } while (0) #define SPIN_LOCK(SPIN) \ do { \ if (pthread_spin_lock((SPIN)) != 0) { \ ISTGT_ERRLOG("lock error\n"); \ pthread_exit(NULL); \ } \ } while (0) #define SPIN_UNLOCK(SPIN) \ do { \ if (pthread_spin_unlock((SPIN)) != 0) { \ ISTGT_ERRLOG("unlock error\n"); \ pthread_exit(NULL); \ } \ } while (0) typedef struct istgt_portal_t { char *label; char *host; char *port; int idx; int tag; int sock; } PORTAL; typedef PORTAL *PORTAL_Ptr; typedef struct istgt_initiator_group_t { int ninitiators; char **initiators; int nnetmasks; char **netmasks; int idx; int tag; } INITIATOR_GROUP; typedef INITIATOR_GROUP *INITIATOR_GROUP_Ptr; typedef enum { ISTGT_STATE_INVALID = 0, ISTGT_STATE_INITIALIZED = 1, ISTGT_STATE_RUNNING = 2, ISTGT_STATE_EXITING = 3, ISTGT_STATE_SHUTDOWN = 4, } ISTGT_STATE; #define DEFAULT_ISTGT_SWMODE ISTGT_SWMODE_NORMAL typedef enum { ISTGT_SWMODE_TRADITIONAL = 0, ISTGT_SWMODE_NORMAL = 1, ISTGT_SWMODE_EXPERIMENTAL = 2, } ISTGT_SWMODE; typedef struct istgt_t { CONFIG *config; char *pidfile; char *authfile; #if 0 char *mediafile; char *livefile; #endif char *mediadirectory; char *nodebase; pthread_attr_t attr; pthread_mutex_t mutex; ISTGT_STATE state; ISTGT_SWMODE swmode; int nuctl_portal; PORTAL uctl_portal[MAX_UCPORTAL]; int nuctl_netmasks; char **uctl_netmasks; int nportal; PORTAL portal[MAX_PORTAL]; int ninitiator_group; INITIATOR_GROUP initiator_group[MAX_INITIATOR_GROUP]; int nlogical_unit; struct istgt_lu_t *logical_unit[MAX_LOGICAL_UNIT]; int timeout; int nopininterval; int maxr2t; int no_discovery_auth; int req_discovery_auth; int req_discovery_auth_mutual; int discovery_auth_group; int no_uctl_auth; int req_uctl_auth; int req_uctl_auth_mutual; int uctl_auth_group; int MaxSessions; int MaxConnections; int MaxOutstandingR2T; int DefaultTime2Wait; int DefaultTime2Retain; int FirstBurstLength; int MaxBurstLength; int MaxRecvDataSegmentLength; int InitialR2T; int ImmediateData; int DataPDUInOrder; int DataSequenceInOrder; int ErrorRecoveryLevel; } ISTGT; typedef ISTGT *ISTGT_Ptr; char *istgt_get_nmval(CF_SECTION *sp, const char *key, int idx1, int idx2); char *istgt_get_nval(CF_SECTION *sp, const char *key, int idx); char *istgt_get_val(CF_SECTION *sp, const char *key); int istgt_get_nintval(CF_SECTION *sp, const char *key, int idx); int istgt_get_intval(CF_SECTION *sp, const char *key); #ifdef USE_ATOMIC static inline int istgt_get_state(ISTGT_Ptr istgt) { ISTGT_STATE state; #if defined HAVE_ATOMIC_LOAD_ACQ_INT state = atomic_load_acq_int((unsigned int *)&istgt->state); #elif defined HAVE_ATOMIC_OR_UINT_NV state = (int)atomic_or_uint_nv((unsigned int *)&istgt->state, 0); #else #error "no atomic operation" #endif return state; } static inline void istgt_set_state(ISTGT_Ptr istgt, ISTGT_STATE state) { #if defined HAVE_ATOMIC_STORE_REL_INT atomic_store_rel_int((unsigned int *)&istgt->state, state); #elif defined HAVE_ATOMIC_SWAP_UINT (void)atomic_swap_uint((unsigned int *)&istgt->state, state); #if defined HAVE_MEMBAR_PRODUCER membar_producer(); #endif #else #error "no atomic operation" #endif } #else static inline int istgt_get_state(ISTGT_Ptr istgt) { ISTGT_STATE state; MTX_LOCK(&istgt->mutex); state = istgt->state; MTX_UNLOCK(&istgt->mutex); return state; } static inline void istgt_set_state(ISTGT_Ptr istgt, ISTGT_STATE state) { MTX_LOCK(&istgt->mutex); istgt->state = state; MTX_UNLOCK(&istgt->mutex); } #endif /* USE_ATOMIC */ #endif /* ISTGT_H */ istgt-20111008/src/istgt_lu_dvd.c000644 000000 000000 00000267444 11530752361 016647 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_UUID_H #include #endif #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_iscsi.h" #include "istgt_lu.h" #include "istgt_proto.h" #include "istgt_scsi.h" //#define ISTGT_TRACE_DVD #define DEFAULT_DVD_BLOCKLEN 2048 #define DEFAULT_DVD_PROFILE MM_PROF_DVDROM enum { MM_PROF_CDROM = 0x0008, MM_PROF_DVDROM = 0x0010, } ISTGT_LU_MM_PROF; typedef struct istgt_lu_dvd_t { ISTGT_LU_Ptr lu; int num; int lun; int fd; const char *file; uint64_t size; uint64_t blocklen; uint64_t blockcnt; #ifdef HAVE_UUID_H uuid_t uuid; #endif /* HAVE_UUID_H */ /* cache flags */ int read_cache; int write_cache; /* flags */ int mflags; /* current DVD/CD profile */ int profile; /* media state */ volatile int mload; volatile int mchanged; volatile int mwait; /* mode flags */ volatile int lock; /* SCSI sense code */ volatile int sense; } ISTGT_LU_DVD; #define BUILD_SENSE(SK,ASC,ASCQ) \ do { \ *sense_len = \ istgt_lu_dvd_build_sense_data(spec, sense_data, \ ISTGT_SCSI_SENSE_ ## SK, \ (ASC), (ASCQ)); \ } while (0) static int istgt_lu_dvd_build_sense_data(ISTGT_LU_DVD *spec, uint8_t *data, int sk, int asc, int ascq); static int istgt_lu_dvd_open(ISTGT_LU_DVD *spec, int flags, int mode) { int rc; rc = open(spec->file, flags, mode); if (rc < 0) { return -1; } spec->fd = rc; return 0; } static int istgt_lu_dvd_close(ISTGT_LU_DVD *spec) { int rc; if (spec->fd == -1) return 0; rc = close(spec->fd); if (rc < 0) { return -1; } spec->fd = -1; return 0; } static int64_t istgt_lu_dvd_seek(ISTGT_LU_DVD *spec, uint64_t offset) { off_t rc; rc = lseek(spec->fd, (off_t) offset, SEEK_SET); if (rc < 0) { return -1; } return 0; } static int64_t istgt_lu_dvd_read(ISTGT_LU_DVD *spec, void *buf, uint64_t nbytes) { int64_t rc; rc = (int64_t) read(spec->fd, buf, (size_t) nbytes); if (rc < 0) { return -1; } return rc; } static int64_t istgt_lu_dvd_write(ISTGT_LU_DVD *spec, const void *buf, uint64_t nbytes) { int64_t rc; rc = (int64_t) write(spec->fd, buf, (size_t) nbytes); if (rc < 0) { return -1; } return rc; } static int64_t istgt_lu_dvd_sync(ISTGT_LU_DVD *spec, uint64_t offset, uint64_t nbytes) { int64_t rc; rc = (int64_t) fsync(spec->fd); if (rc < 0) { return -1; } return rc; } int istgt_lu_dvd_media_present(ISTGT_LU_DVD *spec) { if (spec->mload) { return 1; } return 0; } int istgt_lu_dvd_media_lock(ISTGT_LU_DVD *spec) { if (spec->lock) { return 1; } return 0; } static int istgt_lu_dvd_allocate(ISTGT_LU_DVD *spec); int istgt_lu_dvd_load_media(ISTGT_LU_DVD *spec) { ISTGT_LU_Ptr lu; int flags; int newfile; int rc; if (istgt_lu_dvd_media_present(spec)) { /* media present */ return -1; } if (spec->mchanged) { /* changed soon */ return -1; } lu = spec->lu; if (lu->lun[spec->lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: not removable\n", lu->num); return -1; } if (strcasecmp(lu->lun[spec->lun].u.removable.file, "/dev/null") == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: empty\n", lu->num); spec->file = NULL; spec->size = 0; spec->mflags = 0; spec->blocklen = DEFAULT_DVD_BLOCKLEN; spec->blockcnt = spec->size / spec->blocklen; spec->profile = DEFAULT_DVD_PROFILE; return 0; } spec->file = lu->lun[spec->lun].u.removable.file; spec->size = lu->lun[spec->lun].u.removable.size; spec->mflags = lu->lun[spec->lun].u.removable.flags; //spec->blocklen = lu->blocklen; spec->blocklen = DEFAULT_DVD_BLOCKLEN; spec->blockcnt = spec->size / spec->blocklen; spec->profile = DEFAULT_DVD_PROFILE; spec->mload = 0; spec->mchanged = 1; spec->mwait = 3; if (access(spec->file, W_OK) != 0) { if (errno != ENOENT) { spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY; } } else { struct stat st; rc = stat(spec->file, &st); if (rc != 0 || !S_ISREG(st.st_mode)) { spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY; } else { if ((st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) { spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY; } } } if (lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { flags = O_RDONLY; } else { flags = O_RDWR; } newfile = 0; rc = istgt_lu_dvd_open(spec, flags, 0666); if (rc < 0) { newfile = 1; if (lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { flags = O_RDONLY; } else { flags = (O_CREAT | O_EXCL | O_RDWR); } rc = istgt_lu_dvd_open(spec, flags, 0666); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: open error\n", lu->num, spec->lun); return -1; } if (lu->lun[spec->lun].u.removable.size < ISTGT_LU_MEDIA_SIZE_MIN) { lu->lun[spec->lun].u.removable.size = ISTGT_LU_MEDIA_SIZE_MIN; } } if (lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { /* readonly */ } else { if (newfile == 0) { /* XXX TODO: existing file check */ } rc = istgt_lu_dvd_allocate(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: allocate error\n", lu->num, spec->lun); return -1; } } return 0; } int istgt_lu_dvd_unload_media(ISTGT_LU_DVD *spec) { int64_t rc; if (!istgt_lu_dvd_media_present(spec) && !spec->mchanged) { /* media absent */ return 0; } if (istgt_lu_dvd_media_lock(spec)) { return -1; } if (!spec->lu->readonly && !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { rc = istgt_lu_dvd_sync(spec, 0, spec->size); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_sync() failed\n"); return -1; } } rc = (int64_t) istgt_lu_dvd_close(spec); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_close() failed\n"); return -1; } spec->file = NULL; spec->size = 0; spec->mflags = 0; spec->blocklen = DEFAULT_DVD_BLOCKLEN; spec->blockcnt = spec->size / spec->blocklen; spec->profile = DEFAULT_DVD_PROFILE; spec->mload = 0; spec->mchanged = 0; spec->mwait = 3; return 0; } int istgt_lu_dvd_change_media(ISTGT_LU_DVD *spec, char *type, char *flags, char *file, char *size) { ISTGT_LU_Ptr lu; char *mfile; uint64_t msize; int mflags; int rc; if (istgt_lu_dvd_media_lock(spec)) { return -1; } lu = spec->lu; if (lu->lun[spec->lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: not removable\n", lu->num); return -1; } if (strcmp(type, "-") == 0) { /* use ISO image */ ; } else { ISTGT_ERRLOG("unsupported media type\n"); return -1; } mfile = xstrdup(file); mflags = istgt_lu_parse_media_flags(flags); msize = istgt_lu_parse_media_size(file, size, &mflags); rc = istgt_lu_dvd_unload_media(spec); if (rc < 0) { return -1; } /* replace */ xfree(lu->lun[spec->lun].u.removable.file); lu->lun[spec->lun].u.removable.file = mfile; lu->lun[spec->lun].u.removable.size = msize; lu->lun[spec->lun].u.removable.flags = mflags; /* reload */ rc = istgt_lu_dvd_load_media(spec); if (rc < 0) { (void) istgt_lu_dvd_unload_media(spec); } if (spec->file == NULL) { (void) istgt_lu_dvd_unload_media(spec); } spec->mwait = 5; return rc; } static int istgt_lu_dvd_allocate(ISTGT_LU_DVD *spec) { uint8_t *data; uint64_t fsize; uint64_t size; uint64_t blocklen; uint64_t offset; uint64_t nbytes; int64_t rc; size = spec->size; blocklen = spec->blocklen; nbytes = blocklen; data = xmalloc(nbytes); memset(data, 0, nbytes); fsize = istgt_lu_get_filesize(spec->file); if (fsize > size) { xfree(data); return 0; } offset = size - nbytes; rc = istgt_lu_dvd_seek(spec, offset); if (rc == -1) { ISTGT_ERRLOG("lu_dvd_seek() failed\n"); xfree(data); return -1; } rc = istgt_lu_dvd_read(spec, data, nbytes); /* EOF is OK */ if (rc == -1) { ISTGT_ERRLOG("lu_dvd_read() failed\n"); xfree(data); return -1; } rc = istgt_lu_dvd_seek(spec, offset); if (rc == -1) { ISTGT_ERRLOG("lu_dvd_seek() failed\n"); xfree(data); return -1; } rc = istgt_lu_dvd_write(spec, data, nbytes); if (rc == -1 || rc != nbytes) { ISTGT_ERRLOG("lu_dvd_write() failed\n"); xfree(data); return -1; } xfree(data); return 0; } int istgt_lu_dvd_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { ISTGT_LU_DVD *spec; uint64_t gb_size; uint64_t mb_size; #ifdef HAVE_UUID_H uint32_t status; #endif /* HAVE_UUID_H */ int mb_digit; int ro; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_dvd_init\n"); printf("LU%d DVD UNIT\n", lu->num); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); lu->lun[i].spec = NULL; continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d removable\n", lu->num, i); spec = xmalloc(sizeof *spec); memset(spec, 0, sizeof *spec); spec->lu = lu; spec->num = lu->num; spec->lun = i; spec->fd = -1; spec->read_cache = 1; spec->write_cache = 1; #ifdef HAVE_UUID_H uuid_create(&spec->uuid, &status); if (status != uuid_s_ok) { ISTGT_ERRLOG("LU%d: LUN%d: uuid_create() failed\n", lu->num, i); xfree(spec); return -1; } #endif /* HAVE_UUID_H */ spec->mload = 0; spec->mchanged = 0; spec->mwait = 0; rc = istgt_lu_dvd_load_media(spec); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_load_media() failed\n"); xfree(spec); return -1; } if (spec->file != NULL) { /* initial state */ spec->mload = 1; spec->mchanged = 0; spec->mwait = 0; if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { ro = 1; } else { ro = 0; } printf("LU%d: LUN%d file=%s, size=%"PRIu64", flag=%s\n", lu->num, i, spec->file, spec->size, ro ? "ro" : "rw"); printf("LU%d: LUN%d %"PRIu64" blocks, %"PRIu64" bytes/block\n", lu->num, i, spec->blockcnt, spec->blocklen); gb_size = spec->size / ISTGT_LU_1GB; mb_size = (spec->size % ISTGT_LU_1GB) / ISTGT_LU_1MB; if (gb_size > 0) { mb_digit = (int) (((mb_size * 100) / 1024) / 10); printf("LU%d: LUN%d %"PRIu64".%dGB %sstorage for %s\n", lu->num, i, gb_size, mb_digit, lu->readonly ? "readonly " : "", lu->name); } else { printf("LU%d: LUN%d %"PRIu64"MB %sstorage for %s\n", lu->num, i, mb_size, lu->readonly ? "readonly " : "", lu->name); } } else { /* initial state */ spec->mload = 0; spec->mchanged = 0; spec->mwait = 0; printf("LU%d: LUN%d empty slot\n", lu->num, i); } lu->lun[i].spec = spec; } return 0; } int istgt_lu_dvd_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { ISTGT_LU_DVD *spec; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_dvd_shutdown\n"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } spec = (ISTGT_LU_DVD *) lu->lun[i].spec; if (!spec->lu->readonly && !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { rc = istgt_lu_dvd_sync(spec, 0, spec->size); if (rc < 0) { //ISTGT_ERRLOG("LU%d: lu_dvd_sync() failed\n", lu->num); /* ignore error */ } } rc = istgt_lu_dvd_close(spec); if (rc < 0) { //ISTGT_ERRLOG("LU%d: lu_dvd_close() failed\n", lu->num); /* ignore error */ } xfree(spec); lu->lun[i].spec = NULL; } return 0; } static int istgt_lu_dvd_scsi_report_luns(ISTGT_LU_Ptr lu, CONN_Ptr conn, uint8_t *cdb, int sel, uint8_t *data, int alloc_len) { uint64_t fmt_lun, lun, method; int hlen = 0, len = 0; int i; if (alloc_len < 8) { return -1; } if (sel == 0x00) { /* logical unit with addressing method */ } else if (sel == 0x01) { /* well known logical unit */ } else if (sel == 0x02) { /* logical unit */ } else { return -1; } /* LUN LIST LENGTH */ DSET32(&data[0], 0); /* Reserved */ DSET32(&data[4], 0); hlen = 8; for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); #endif continue; } if (alloc_len - (hlen + len) < 8) { return -1; } lun = (uint64_t) i; if (lu->maxlun <= 0x0100) { /* below 256 */ method = 0x00U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (lun & 0x00ffU) << 48; } else if (lu->maxlun <= 0x4000U) { /* below 16384 */ method = 0x01U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (lun & 0x3fffU) << 48; } else { /* XXX */ fmt_lun = 0; } /* LUN */ DSET64(&data[hlen + len], fmt_lun); len += 8; } /* LUN LIST LENGTH */ DSET32(&data[0], len); return hlen + len; } static int istgt_lu_dvd_scsi_inquiry(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int alloc_len) { uint64_t LUI; uint8_t *cp, *cp2; int hlen = 0, len = 0, plen, plen2; int pc; int pq, pd; int rmb; int evpd; int pg_tag; int i, j; if (alloc_len < 0xff) { return -1; } pq = 0x00; pd = SPC_PERIPHERAL_DEVICE_TYPE_DVD; rmb = 1; LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU); pc = cdb[2]; evpd = BGET8(&cdb[1], 0); if (evpd) { /* Vital product data */ switch (pc) { case SPC_VPD_SUPPORTED_VPD_PAGES: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; data[4] = SPC_VPD_SUPPORTED_VPD_PAGES; /* 0x00 */ data[5] = SPC_VPD_UNIT_SERIAL_NUMBER; /* 0x80 */ data[6] = SPC_VPD_DEVICE_IDENTIFICATION; /* 0x83 */ data[7] = SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES; /* 0x85 */ data[8] = SPC_VPD_EXTENDED_INQUIRY_DATA; /* 0x86 */ data[9] = SPC_VPD_MODE_PAGE_POLICY; /* 0x87 */ data[10]= SPC_VPD_SCSI_PORTS; /* 0x88 */ len = 11 - hlen; /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_UNIT_SERIAL_NUMBER: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; /* PRODUCT SERIAL NUMBER */ len = strlen(spec->lu->inq_serial); if (len > MAX_LU_SERIAL_STRING) { len = MAX_LU_SERIAL_STRING; } istgt_strcpy_pad(&data[4], len, spec->lu->inq_serial, ' '); /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_DEVICE_IDENTIFICATION: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Identification descriptor 1 */ /* Logical Unit */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_NAA, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ #if 0 /* 16bytes ID */ plen = istgt_lu_set_extid(&cp[4], 0, LUI); #else plen = istgt_lu_set_lid(&cp[4], LUI); #endif cp[3] = plen; len += 4 + plen; /* Identification descriptor 2 */ /* T10 VENDOR IDENTIFICATION */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* T10 VENDOR IDENTIFICATION */ istgt_strcpy_pad(&cp[4], 8, spec->lu->inq_vendor, ' '); plen = 8; /* VENDOR SPECIFIC IDENTIFIER */ /* PRODUCT IDENTIFICATION */ istgt_strcpy_pad(&cp[12], 16, spec->lu->inq_product, ' '); /* PRODUCT SERIAL NUMBER */ istgt_strcpy_pad(&cp[28], MAX_LU_SERIAL_STRING, spec->lu->inq_serial, ' '); plen += 16 + MAX_LU_SERIAL_STRING; cp[3] = plen; len += 4 + plen; /* Identification descriptor 3 */ /* Target Device */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_DEVICE, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp[4], MAX_TARGET_NAME, "%s", spec->lu->name); cp[3] = plen; len += 4 + plen; /* Identification descriptor 4 */ /* Target Port */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp[4], MAX_TARGET_NAME, "%s"",t,0x""%4.4x", spec->lu->name, conn->portal.tag); cp[3] = plen; len += 4 + plen; /* Identification descriptor 5 */ /* Relative Target Port */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Obsolete */ DSET16(&cp[4], 0); /* Relative Target Port Identifier */ //DSET16(&cp[6], 1); /* port1 as port A */ //DSET16(&cp[6], 2); /* port2 as port B */ DSET16(&cp[6], (uint16_t) (1 + conn->portal.idx)); plen = 4; cp[3] = plen; len += 4 + plen; /* Identification descriptor 6 */ /* Target port group */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Reserved */ DSET16(&cp[4], 0); /* TARGET PORT GROUP */ DSET16(&cp[6], (uint16_t) (conn->portal.tag)); plen = 4; cp[3] = plen; len += 4 + plen; /* Identification descriptor 7 */ /* Logical unit group */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Reserved */ DSET16(&cp[4], 0); /* LOGICAL UNIT GROUP */ DSET16(&cp[6], (uint16_t) (spec->lu->num)); plen = 4; cp[3] = plen; len += 4 + plen; /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_EXTENDED_INQUIRY_DATA: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; /* RTO(3) GRD_CHK(2) APP_CHK(1) REF_CHK(0) */ data[4] = 0; /* GROUP_SUP(4) PRIOR_SUP(3) HEADSUP(2) ORDSUP(1) SIMPSUP(0) */ data[5] = 0; /* NV_SUP(1) V_SUP(0) */ data[6] = 0; /* Reserved[7-63] */ memset(&data[7], 0, (64 - 7)); len = 64 - hlen; /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; #if 0 /* Network services descriptor N */ cp = &data[hlen + len]; /* ASSOCIATION(6-5) SERVICE TYPE(4-0) */ BDSET8W(&cp[0], 0x00, 6, 2); BDADD8W(&cp[0], 0x00, 4, 5); /* Reserved */ cp[1] = 0; /* NETWORK ADDRESS LENGTH */ DSET16(&cp[2], 0); /* NETWORK ADDRESS */ cp[4] = 0; /* ... */ plen = 0; DSET16(&cp[2], plen); len += 4 + plen; #endif /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_MODE_PAGE_POLICY: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Mode page policy descriptor 1 */ cp = &data[hlen + len]; /* POLICY PAGE CODE(5-0) */ BDSET8W(&cp[0], 0x3f, 5, 6); /* all page code */ /* POLICY SUBPAGE CODE */ cp[1] = 0xff; /* all sub page */ /* MLUS(7) MODE PAGE POLICY(1-0) */ //BDSET8(&cp[2], 1, 7); /* multiple logical units share */ BDSET8(&cp[2], 0, 7); /* own copy */ BDADD8W(&cp[2], 0x00, 1, 2); /* Shared */ //BDADD8W(&cp[2], 0x01, 1, 2); /* Per target port */ //BDADD8W(&cp[2], 0x02, 1, 2); /* Per initiator port */ //BDADD8W(&cp[2], 0x03, 1, 2); /* Per I_T nexus */ /* Reserved */ cp[3] = 0; len += 4; /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_SCSI_PORTS: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Identification descriptor list */ for (i = 0; i < spec->lu->maxmap; i++) { pg_tag = spec->lu->map[i].pg_tag; /* skip same pg_tag */ for (j = 0; j < i; j++) { if (spec->lu->map[j].pg_tag == pg_tag) { goto skip_pg_tag; } } /* Identification descriptor N */ cp = &data[hlen + len]; /* Reserved */ DSET16(&cp[0], 0); /* RELATIVE PORT IDENTIFIER */ DSET16(&cp[2], (uint16_t) (1 + pg_tag)); /* Reserved */ DSET16(&cp[4], 0); /* INITIATOR PORT TRANSPORTID LENGTH */ DSET16(&cp[6], 0); /* Reserved */ DSET16(&cp[8], 0); /* TARGET PORT DESCRIPTORS LENGTH */ DSET16(&cp[10], 0); len += 12; plen2 = 0; /* Target port descriptor 1 */ cp2 = &data[hlen + len + plen2]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp2[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp2[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp2[1], 1, 7, 1); /* PIV */ BDADD8W(&cp2[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp2[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp2[2] = 0; /* IDENTIFIER LENGTH */ cp2[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp2[4], MAX_TARGET_NAME, "%s"",t,0x""%4.4x", spec->lu->name, pg_tag); cp2[3] = plen; plen2 += 4 + plen; /* TARGET PORT DESCRIPTORS LENGTH */ DSET16(&cp[10], plen2); len += plen2; skip_pg_tag: ; } /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; default: if (pc >= 0xc0 && pc <= 0xff) { ISTGT_WARNLOG("Vendor specific INQUIRY VPD page 0x%x\n", pc); } else { ISTGT_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc); } return -1; } } else { /* Standard INQUIRY data */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* RMB(7) */ BDSET8W(&data[1], rmb, 7, 1); /* VERSION */ /* See SPC3/SBC2/MMC4/SAM2 for more details */ data[2] = SPC_VERSION_SPC3; /* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */ BDSET8W(&data[3], 2, 3, 4); /* format 2 */ BDADD8(&data[1], 1, 4); /* hierarchical support */ /* ADDITIONAL LENGTH */ data[4] = 0; hlen = 5; /* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */ data[5] = 0; /* BQUE(7) ENCSERV(6) VS(5) MULTIP(4) MCHNGR(3) ADDR16(0) */ data[6] = 0; /* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */ data[7] = 0; /* T10 VENDOR IDENTIFICATION */ istgt_strcpy_pad(&data[8], 8, spec->lu->inq_vendor, ' '); /* PRODUCT IDENTIFICATION */ istgt_strcpy_pad(&data[16], 16, spec->lu->inq_product, ' '); /* PRODUCT REVISION LEVEL */ istgt_strcpy_pad(&data[32], 4, spec->lu->inq_revision, ' '); /* Vendor specific */ memset(&data[36], 0x20, 20); /* CLOCKING(3-2) QAS(1) IUS(0) */ data[56] = 0; /* Reserved */ data[57] = 0; /* VERSION DESCRIPTOR 1-8 */ DSET16(&data[58], 0x0960); /* iSCSI (no version claimed) */ DSET16(&data[60], 0x0300); /* SPC-3 (no version claimed) */ DSET16(&data[62], 0x03a0); /* MMC-4 (no version claimed) */ DSET16(&data[64], 0x0040); /* SAM-2 (no version claimed) */ DSET16(&data[66], 0x0000); DSET16(&data[68], 0x0000); DSET16(&data[70], 0x0000); DSET16(&data[72], 0x0000); /* Reserved[74-95] */ memset(&data[74], 0, (96 - 74)); /* Vendor specific parameters[96-n] */ //data[96] = 0; len = 96 - hlen; /* ADDITIONAL LENGTH */ data[4] = len; } return hlen + len; } #define MODE_SENSE_PAGE_INIT(B,L,P,SP) \ do { \ memset((B), 0, (L)); \ if ((SP) != 0x00) { \ (B)[0] = (P) | 0x40; /* PAGE + SPF=1 */ \ (B)[1] = (SP); \ DSET16(&(B)[2], (L) - 4); \ } else { \ (B)[0] = (P); \ (B)[1] = (L) - 2; \ } \ } while (0) static int istgt_lu_dvd_scsi_mode_sense_page(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int len = 0; int plen; int i; #if 0 printf("SENSE pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage); #endif ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE: pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage); if (pc == 0x00) { /* Current values */ } else if (pc == 0x01) { /* Changeable values */ if (page != 0x08) { /* not supported */ return 0; } } else if (pc == 0x02) { /* Default values */ } else { /* Saved values */ } cp = &data[len]; switch (page) { case 0x00: /* Vendor specific */ break; case 0x01: /* Read-Write Error Recovery */ break; case 0x02: /* Reserved */ break; case 0x03: /* MRW */ break; case 0x04: /* Reserved */ break; case 0x05: /* Write Parameter */ break; case 0x06: /* Reserved */ break; case 0x07: /* Verify Error Recovery */ break; case 0x08: /* Caching */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Caching\n"); if (subpage != 0x00) break; plen = 0x12 + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); BDADD8(&cp[0], 1, 7); /* PS */ BDADD8(&cp[2], 1, 2); /* WCE */ //BDADD8(&cp[2], 1, 0); /* RCD */ if (spec->write_cache == 1) { BDADD8(&cp[2], 1, 2); /* WCE=1 */ } else { BDADD8(&cp[2], 0, 2); /* WCE=0 */ } if (spec->read_cache == 0) { BDADD8(&cp[2], 1, 0); /* RCD=1 */ } else { BDADD8(&cp[2], 0, 0); /* RCD=0 */ } len += plen; break; case 0x09: case 0x0a: /* Reserved */ break; case 0x0b: /* Medium Types Supported */ break; case 0x0c: /* Reserved */ break; case 0x0d: /* CD Device Parameters */ break; case 0x0e: /* CD Audio Control */ break; case 0x0f: case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: case 0x18: case 0x19: /* Reserved */ break; case 0x1a: /* Power Condition */ break; case 0x1b: /* Reserved */ break; case 0x1c: /* Informational Exceptions Control */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Informational Exceptions Control\n"); if (subpage != 0x00) break; plen = 0x0a + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x1d: /* Time-out & Protect */ break; case 0x1e: case 0x1f: /* Reserved */ break; case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: /* Vendor-specific */ break; case 0x2a: /* MM Capabilities & Mechanical Status */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE MM Capabilities & Mechanical Status\n"); if (subpage != 0x00) break; plen = 28 + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); BDADD8(&cp[2], 1, 3); /* DVD-ROM read */ len += plen; break; case 0x2b: /* Reserved */ break; case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: /* Vendor-specific */ break; case 0x3f: switch (subpage) { case 0x00: /* All mode pages */ for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len); } break; case 0xff: /* All mode pages and subpages */ for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len); } for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0xff, &cp[len], alloc_len); } break; default: /* 0x01-0x3e: Reserved */ break; } } return len; } static int istgt_lu_dvd_scsi_mode_sense6(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int hlen = 0, len = 0, plen; int total; int llbaa = 0; data[0] = 0; /* Mode Data Length */ if (spec->mload) { data[1] = 0; /* Medium Type */ data[2] = 0; /* Device-Specific Parameter */ if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { BDADD8(&data[2], 1, 7); /* WP */ } } else { data[1] = 0; /* Medium Type */ data[2] = 0; /* Device-Specific Parameter */ } data[3] = 0; /* Block Descripter Length */ hlen = 4; cp = &data[4]; if (dbd) { /* Disable Block Descripters */ len = 0; } else { if (llbaa) { if (spec->mload) { /* Number of Blocks */ DSET64(&cp[0], spec->blockcnt); /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], (uint32_t) spec->blocklen); } else { /* Number of Blocks */ DSET64(&cp[0], 0ULL); /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], 0); } len = 16; } else { if (spec->mload) { /* Number of Blocks */ if (spec->blockcnt > 0xffffffffULL) { DSET32(&cp[0], 0xffffffffUL); } else { DSET32(&cp[0], (uint32_t) spec->blockcnt); } /* Block Length */ DSET32(&cp[4], (uint32_t) spec->blocklen); } else { /* Number of Blocks */ DSET32(&cp[0], 0); /* Block Length */ DSET32(&cp[4], 0); } len = 8; } cp += len; } data[3] = len; /* Block Descripter Length */ plen = istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len); cp += plen; total = hlen + len + plen; data[0] = total - 1; /* Mode Data Length */ return total; } static int istgt_lu_dvd_scsi_mode_sense10(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int llbaa, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int hlen = 0, len = 0, plen; int total; DSET16(&data[0], 0); /* Mode Data Length */ if (spec->mload) { data[2] = 0; /* Medium Type */ data[3] = 0; /* Device-Specific Parameter */ if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { BDADD8(&data[3], 1, 7); /* WP */ } } else { data[2] = 0; /* Medium Type */ data[3] = 0; /* Device-Specific Parameter */ } if (llbaa) { BDSET8(&data[4], 1, 1); /* Long LBA */ } else { BDSET8(&data[4], 0, 1); /* Short LBA */ } data[5] = 0; /* Reserved */ DSET16(&data[6], 0); /* Block Descripter Length */ hlen = 8; cp = &data[8]; if (dbd) { /* Disable Block Descripters */ len = 0; } else { if (llbaa) { if (spec->mload) { /* Number of Blocks */ DSET64(&cp[0], spec->blockcnt); /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], (uint32_t) spec->blocklen); } else { /* Number of Blocks */ DSET64(&cp[0], 0ULL); /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], 0); } len = 16; } else { if (spec->mload) { /* Number of Blocks */ if (spec->blockcnt > 0xffffffffULL) { DSET32(&cp[0], 0xffffffffUL); } else { DSET32(&cp[0], (uint32_t) spec->blockcnt); } /* Block Length */ DSET32(&cp[4], (uint32_t) spec->blocklen); } else { /* Number of Blocks */ DSET32(&cp[0], 0); /* Block Length */ DSET32(&cp[4], 0); } len = 8; } cp += len; } DSET16(&data[6], len); /* Block Descripter Length */ plen = istgt_lu_dvd_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len); cp += plen; total = hlen + len + plen; DSET16(&data[0], total - 2); /* Mode Data Length */ return total; } static int istgt_lu_dvd_transfer_data(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *buf, size_t bufsize, size_t len) { int rc; if (len > bufsize) { ISTGT_ERRLOG("bufsize(%d) too small\n", bufsize); return -1; } rc = istgt_iscsi_transfer_out(conn, lu_cmd, buf, bufsize, len); if (rc < 0) { ISTGT_ERRLOG("iscsi_transfer_out()\n"); return -1; } return 0; } static int istgt_lu_dvd_scsi_mode_select_page(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int pf, int sp, uint8_t *data, size_t len) { int ps, spf, page, subpage; int hlen, plen; int rc; if (pf == 0) { /* vendor specific */ return 0; } if (len < 1) return 0; ps = BGET8(&data[0], 7); spf = BGET8(&data[0], 6); page = data[0] & 0x3f; if (spf) { /* Sub_page mode page format */ hlen = 4; if (len < hlen) return 0; subpage = data[1]; plen = DGET16(&data[2]); } else { /* Page_0 mode page format */ hlen = 2; if (len < hlen) return 0; subpage = 0; plen = data[1]; } plen += hlen; if (len < plen) return 0; #if 0 printf("ps=%d, page=%2.2x, subpage=%2.2x\n", ps, page, subpage); #endif switch (page) { case 0x08: /* Caching */ { int wce, rcd; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Caching\n"); if (subpage != 0x00) break; if (plen != 0x12 + hlen) { /* unknown format */ break; } wce = BGET8(&data[2], 2); /* WCE */ rcd = BGET8(&data[2], 0); /* RCD */ if (wce) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Writeback cache enable\n"); spec->write_cache = 1; } else { spec->write_cache = 0; } if (rcd) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Read cache disable\n"); spec->read_cache = 0; } else { spec->read_cache = 1; } } break; default: /* not supported */ break; } len -= plen; if (len != 0) { rc = istgt_lu_dvd_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[plen], len); if (rc < 0) { return rc; } } return 0; } #define FEATURE_DESCRIPTOR_INIT(B,L,FC) \ do { \ memset((B), 0, (L)); \ DSET16(&(B)[0], (FC)); \ (B)[3] = (L) - 4; \ } while (0) static int istgt_lu_dvd_get_feature_descriptor(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int fc, uint8_t *data) { uint8_t *cp; int hlen = 0, len = 0, plen; switch (fc) { case 0x0000: /* Profile List */ plen = 2 * 4 + 4; FEATURE_DESCRIPTOR_INIT(data, plen, fc); /* Version(5-2) Persistent(1) Current(0) */ BDSET8W(&data[2], 0, 5, 4); BSET8(&data[2], 1); /* Persistent=1 */ BSET8(&data[2], 0); /* Current=1 */ hlen = 4; /* Profile Descriptor */ cp = &data[hlen + len]; /* Profile 1 (CDROM) */ DSET16(&cp[0], 0x0008); if (spec->profile == MM_PROF_CDROM) { BSET8(&cp[2], 0); /* CurrentP(0)=1 */ } plen = 4; len += plen; cp = &data[hlen + len]; /* Profile 2 (DVDROM) */ DSET16(&cp[0], 0x0010); if (spec->profile == MM_PROF_DVDROM) { BSET8(&cp[2], 0); /* CurrentP(0)=1 */ } plen = 4; len += plen; break; case 0x0001: /* Core Feature */ /* GET CONFIGURATION/GET EVENT STATUS NOTIFICATION/INQUIRY */ /* MODE SELECT (10)/MODE SENSE (10)/REQUEST SENSE/TEST UNIT READY */ plen = 8 + 4; FEATURE_DESCRIPTOR_INIT(data, plen, fc); /* Version(5-2) Persistent(1) Current(0) */ BDSET8W(&data[2], 0x01, 5, 4); /* MMC4 */ BSET8(&data[2], 1); /* Persistent=1 */ BSET8(&data[2], 0); /* Current=1 */ hlen = 4; /* Physical Interface Standard */ DSET32(&data[4], 0x00000000); /* Unspecified */ /* DBE(0) */ BCLR8(&data[8], 0); /* DBE=0*/ len = 8; break; case 0x0003: /* Removable Medium */ /* MECHANISM STATUS/PREVENT ALLOW MEDIUM REMOVAL/START STOP UNIT */ plen = 0x04 + 4; FEATURE_DESCRIPTOR_INIT(data, plen, fc); /* Version(5-2) Persistent(1) Current(0) */ BDSET8W(&data[2], 0x01, 5, 4); BSET8(&data[2], 1); /* Persistent=1 */ BSET8(&data[2], 0); /* Current=1 */ hlen = 4; /* Loading Mechanism Type(7-5) Eject(3) Pvnt Jmpr(2) Lock(0) */ BDSET8W(&data[4], 0x01, 7, 3); /* Tray type loading mechanism */ BSET8(&data[4], 3); /* eject via START/STOP YES */ BSET8(&data[4], 0); /* locking YES */ len = 8; break; case 0x0010: /* Random Readable */ /* READ CAPACITY/READ (10) */ plen = 4 + 4; FEATURE_DESCRIPTOR_INIT(data, plen, fc); /* Version(5-2) Persistent(1) Current(0) */ BDSET8W(&data[2], 0x00, 5, 4); BSET8(&data[2], 1); /* Persistent=1 */ BSET8(&data[2], 0); /* Current=1 */ hlen = 4; /* Logical Block Size */ DSET32(&data[4], (uint32_t) spec->blocklen); /* Blocking */ DSET16(&data[8], 1); /* PP(0) */ BCLR8(&data[10], 0); /* PP=0 */ len = 4; break; case 0x001d: /* Multi-Read Feature */ /* READ (10)/READ CD/READ DISC INFORMATION/READ TRACK INFORMATION */ plen = 4; FEATURE_DESCRIPTOR_INIT(data, plen, fc); /* Version(5-2) Persistent(1) Current(0) */ BDSET8W(&data[2], 0x00, 5, 4); BSET8(&data[2], 1); /* Persistent=1 */ BSET8(&data[2], 0); /* Current=1 */ hlen = 4; len = 0; break; case 0x001e: /* CD Read */ /* READ CD/READ CD MSF/READ TOC/PMA/ATIP */ plen = 4 + 4; FEATURE_DESCRIPTOR_INIT(data, plen, fc); /* Version(5-2) Persistent(1) Current(0) */ BDSET8W(&data[2], 0x02, 5, 4); /* MMC4 */ BCLR8(&data[2], 1); /* Persistent=0 */ if (spec->profile == MM_PROF_CDROM) { BSET8(&data[2], 0); /* Current=1 */ } else { BCLR8(&data[2], 0); /* Current=0 */ } hlen = 4; /* DAP(7) C2 Flags(1) CD-Text(0) */ BCLR8(&data[4], 7); /* not support DAP */ BCLR8(&data[4], 1); /* not support C2 */ BCLR8(&data[4], 0); /* not support CD-Text */ len = 4; break; case 0x001f: /* DVD Read */ /* READ (10)/READ (12)/READ DVD STRUCTURE/READ TOC/PMA/ATIP */ plen = 4; FEATURE_DESCRIPTOR_INIT(data, plen, fc); /* Version(5-2) Persistent(1) Current(0) */ BDSET8W(&data[2], 0x00, 5, 4); BCLR8(&data[2], 1); /* Persistent=0 */ if (spec->profile == MM_PROF_DVDROM) { BSET8(&data[2], 0); /* Current=1 */ } else { BCLR8(&data[2], 0); /* Current=0 */ } hlen = 4; len = 0; break; default: /* not supported */ break; } return hlen + len; } static int istgt_lu_dvd_scsi_get_configuration(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int rt, int sfn, uint8_t *data) { uint8_t *cp; int hlen = 0, len = 0, plen; int fc; /* Feature Header */ /* Data Length */ DSET32(&data[0], 0); /* Reserved */ data[4] = 0; /* Reserved */ data[5] = 0; /* Current Profile */ DSET16(&data[6], spec->profile); hlen = 8; cp = &data[hlen]; switch (rt) { case 0x00: /* all of features */ for (fc = sfn; fc < 0xffff; fc++) { plen = istgt_lu_dvd_get_feature_descriptor(spec, conn, cdb, fc, &cp[len]); len += plen; } break; case 0x01: /* current of features */ for (fc = sfn; fc < 0xffff; fc++) { plen = istgt_lu_dvd_get_feature_descriptor(spec, conn, cdb, fc, &cp[len]); if (BGET8(&cp[2], 0) == 1) { len += plen; } else { /* drop non active descriptors */ } } break; case 0x02: /* specified feature */ fc = sfn; plen = istgt_lu_dvd_get_feature_descriptor(spec, conn, cdb, fc, &cp[len]); len += plen; break; default: /* not supported */ break; } /* Data Length */ DSET32(&data[0], len); return hlen + len; } static int istgt_lu_dvd_scsi_get_event_status_notification(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int keep, int ncr, uint8_t *data) { uint8_t *cp; int hlen = 0, len = 0; /* Event Descriptor Length */ DSET16(&data[0], 0); /* NEA(7) Notification Class(2-0) */ data[2] = 0; /* Supported Event Classes */ data[3] = 0x7e; hlen = 4; cp = &data[hlen]; /* Lowest class number has highest priority */ if (ncr & (1 << 0)) { /* Reserved */ len = 0; } if (ncr & (1 << 1)) { /* Operational Change */ BDSET8W(&data[2], 0x01, 2, 3); /* Notification Class */ /* Event Code */ BDSET8W(&cp[0], 0x00, 3, 4); /* NoChg */ /* Persistent Prevented(7) Operational Status(3-0) */ BDSET8(&cp[1], 0, 7); /* not prevented */ BDADD8W(&cp[1], 0, 3, 4); /* Operational Change */ DSET16(&cp[2], 0x00); /* NoChg */ len = 4; goto event_available; } if (ncr & (1 << 2)) { /* Power Management */ BDSET8W(&data[2], 0x02, 2, 3); /* Notification Class */ /* Event Code */ BDSET8W(&cp[0], 0x00, 3, 4); /* NoChg */ /* Power Status */ cp[1] = 0x01; /* Active */ /* Reserved */ cp[2] = 0; /* Reserved */ cp[3] = 0; len = 4; goto event_available; } if (ncr & (1 << 3)) { /* External Request */ BDSET8W(&data[2], 0x03, 2, 3); /* Notification Class */ /* Event Code */ BDSET8W(&cp[0], 0x00, 3, 4); /* NoChg */ /* Persistent Prevented(7) External Request Status(3-0) */ BDSET8(&cp[1], 0, 7); /* not prevented */ BDADD8W(&cp[1], 0, 3, 4); /* Ready */ /* External Request */ DSET16(&cp[2], 0x00); /* No Request */ len = 4; goto event_available; } if (ncr & (1 << 4)) { /* Media */ BDSET8W(&data[2], 0x04, 2, 3); /* Notification Class */ if (spec->mchanged) { if (spec->mwait > 0) { spec->mwait--; } else { spec->mchanged = 0; spec->mload = 1; } if (spec->mload) { /* Event Code */ BDSET8W(&cp[0], 0x02, 3, 4); /* NewMedia */ /* Media Status */ /* Media Present(1) Door or Tray open(0) */ BDSET8(&cp[1], 1, 1); /* media present */ BDADD8(&cp[1], 0, 0); /* tray close */ } else { /* Event Code */ BDSET8W(&cp[0], 0x03, 3, 4); /* MediaRemoval */ /* Media Status */ /* Media Present(1) Door or Tray open(0) */ BDSET8(&cp[1], 0, 1); /* media absent */ BDADD8(&cp[1], 1, 0); /* tray open */ } } else { if (spec->mwait > 0) { spec->mwait--; /* Event Code */ BDSET8W(&cp[0], 0x01, 3, 4); /* EjectRequest */ /* Media Status */ /* Media Present(1) Door or Tray open(0) */ BDSET8(&cp[1], 0, 1); /* media absent */ BDADD8(&cp[1], 1, 0); /* tray open */ } else { if (spec->mload) { /* Event Code */ BDSET8W(&cp[0], 0x00, 3, 4); /* NoChg */ /* Media Status */ /* Media Present(1) Door or Tray open(0) */ BDSET8(&cp[1], 1, 1); /* media present */ BDADD8(&cp[1], 0, 0); /* tray close */ } else { /* Event Code */ BDSET8W(&cp[0], 0x00, 3, 4); /* NoChg */ /* Media Status */ /* Media Present(1) Door or Tray open(0) */ BDSET8(&cp[1], 0, 1); /* media absent */ BDADD8(&cp[1], 0, 0); /* tray close */ } } } /* Start Slot */ cp[2] = 0; /* End Slot */ cp[3] = 0; len = 4; goto event_available; } if (ncr & (1 << 5)) { /* Multi-Initiator */ BDSET8W(&data[2], 0x05, 2, 3); /* Notification Class */ /* Event Code */ BDSET8W(&cp[0], 0x00, 3, 4); /* NoChg */ /* Persistent Prevented(7) Multiple Initiator Status(3-0) */ BDSET8(&cp[1], 0, 7); /* not prevented */ BDADD8W(&cp[1], 0, 3, 4); /* Ready */ /* Multiple Initiator Priority */ DSET16(&cp[2], 0x00); /* No Request */ len = 4; goto event_available; } if (ncr & (1 << 6)) { /* Device Busy */ BDSET8W(&data[2], 0x06, 2, 3); /* Notification Class */ /* Event Code */ BDSET8W(&cp[0], 0x00, 3, 4); /* NoChg */ /* Media Status */ /* Device Busy Status */ cp[1] = 0; /* Not Busy */ /* Time */ DSET16(&cp[2], 0); len = 4; goto event_available; } if (ncr & (1 << 7)) { /* Reserved */ len = 0; } if (len == 0) { /* No Event Available */ BDSET8(&data[2], 0, 7); /* NEA=1 */ } event_available: /* Event Descriptor Length */ DSET16(&data[0], len + (hlen - 2)); return hlen + len; } static int istgt_lu_dvd_scsi_mechanism_status(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data) { uint8_t *cp; int hlen = 0, len = 0, plen; int selected_slot = 0, max_slots = 1; /* Mechanism Status Header */ /* Fault(7) Changer State(6-5) Current Slot(4-0) */ BDSET8(&data[0], 0, 7); BDADD8W(&data[0], 0x00, 6, 2); /* Ready */ BDADD8W(&data[0], (selected_slot & 0x1f), 4, 5); /* slot low bits */ /* Mechanism State(7-5) Door open(4) Current Slot(2-0) */ BDSET8W(&data[1], 0x00, 7, 3); /* Idle */ BDADD8W(&data[1], (selected_slot & 0xe0) >> 5, 2, 3); /* slot high bits */ /* Current LBA (Legacy) */ DSET24(&data[2], 0); /* Number of Slots Available */ data[5] = max_slots; /* Length of Slot Tables */ DSET16(&data[6], 0); hlen = 8; /* Slot Tables */ /* Slot 0 */ cp = &data[hlen + len]; if (spec->mchanged) { if (spec->mload) { /* Disc Present(7) Change(0) */ BDSET8(&cp[0], 1, 7); /* disc in slot */ } else { /* Disc Present(7) Change(0) */ BDSET8(&cp[0], 0, 7); /* no disc in slot */ } BDADD8(&cp[0], 1, 0); /* disc changed */ } else { if (spec->mload) { /* Disc Present(7) Change(0) */ BDSET8(&cp[0], 1, 7); /* disc in slot */ } else { /* Disc Present(7) Change(0) */ BDSET8(&cp[0], 0, 7); /* no disc in slot */ } BDADD8(&cp[0], 0, 0); /* disc not changed */ } /* CWP_V(1) CWP(0) */ BDSET8(&cp[1], 0, 1); /* non Cartridge Write Protection */ BDADD8(&cp[1], 0, 0); /* CWP=0 */ /* Reserved */ cp[2] = 0; /* Reserved */ cp[3] = 0; plen = 4; len += plen; /* Length of Slot Tables */ DSET16(&data[6], len); return hlen + len; } static int istgt_lu_dvd_scsi_read_toc(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int msf, int format, int track, uint8_t *data) { uint8_t *cp; int hlen = 0, len = 0, plen; switch (format) { case 0x00: /* Formatted TOC */ /* TOC Data Length */ DSET16(&data[0], 0); /* First Track Number */ data[2] = 1; /* Last Track Number */ data[3] = 1; hlen = 4; /* TOC Track Descriptor */ /* Track 1 Descriptor */ cp = &data[hlen + len]; /* Reserved */ cp[0] = 0; /* ADR(7-4) CONTROL(3-0) */ cp[1] = 0x14; /* Track Number */ cp[2] = 1; /* Reserved */ cp[3] = 0; /* Track Start Address */ if (msf) { DSET32(&cp[4], istgt_lba2msf(0)); } else { DSET32(&cp[4], 0); } plen = 8; len += plen; /* Track AAh (Lead-out) Descriptor */ cp = &data[hlen + len]; /* Reserved */ cp[0] = 0; /* ADR(7-4) CONTROL(3-0) */ cp[1] = 0x14; /* Track Number */ cp[2] = 0xaa; /* Reserved */ cp[3] = 0; /* Track Start Address */ if (msf) { DSET32(&cp[4], istgt_lba2msf(spec->blockcnt)); } else { DSET32(&cp[4], spec->blockcnt); } plen = 8; len += plen; /* TOC Data Length */ DSET16(&data[0], hlen + len - 2); break; case 0x01: /* Multi-session Information */ /* TOC Data Length */ DSET16(&data[0], 0); /* First Complete Session Number */ data[2] = 1; /* Last Complete Session Number */ data[3] = 1; hlen = 4; /* TOC Track Descriptor */ cp = &data[hlen + len]; /* Reserved */ cp[0] = 0; /* ADR(7-4) CONTROL(3-0) */ cp[1] = 0x14; /* First Track Number In Last Complete Session */ cp[2] = 1; /* Reserved */ cp[3] = 0; /* Start Address of First Track in Last Session */ if (msf) { DSET32(&cp[4], istgt_lba2msf(0)); } else { DSET32(&cp[4], 0); } len = 8; /* TOC Data Length */ DSET16(&data[0], hlen + len - 2); break; case 0x02: /* Raw TOC */ /* TOC Data Length */ DSET16(&data[0], 0); /* First Complete Session Number */ data[2] = 1; /* Last Complete Session Number */ data[3] = 1; hlen = 4; /* TOC Track Descriptor */ /* First Track number in the program area */ cp = &data[hlen + len]; /* Session Number */ cp[0] = 1; /* ADR(7-4) CONTROL(3-0) */ cp[1] = 0x14; /* TNO */ cp[2] = 0; /* POINT */ cp[3] = 0xa0; /* Min */ cp[4] = 0; /* Sec */ cp[5] = 0; /* Frame */ cp[6] = 0; /* Zero */ cp[7] = 0; /* PMIN / First Track Number */ cp[8] = 1; /* PSEC / Disc Type */ cp[9] = 0x00; /* CD-DA or CD Data with first track in Mode 1 */ /* PFRAME */ cp[10] = 0; plen = 11; len += plen; /* Last Track number in the program area */ cp = &data[hlen + len]; /* Session Number */ cp[0] = 1; /* ADR(7-4) CONTROL(3-0) */ cp[1] = 0x14; /* TNO */ cp[2] = 0; /* POINT */ cp[3] = 0xa1; /* Min */ cp[4] = 0; /* Sec */ cp[5] = 0; /* Frame */ cp[6] = 0; /* Zero */ cp[7] = 0; /* PMIN / Last Track Number */ cp[8] = 1; /* PSEC */ cp[9] = 0; /* PFRAME */ cp[10] = 0; plen = 11; len += plen; /* Start location of the Lead-out area */ cp = &data[hlen + len]; /* Session Number */ cp[0] = 1; /* ADR(7-4) CONTROL(3-0) */ cp[1] = 0x14; /* TNO */ cp[2] = 0; /* POINT */ cp[3] = 0xa2; /* Min */ cp[4] = 0; /* Sec */ cp[5] = 0; /* Frame */ cp[6] = 0; /* Zero */ cp[7] = 0; /* PMIN / Start position of Lead-out */ /* PSEC / Start position of Lead-out */ /* PFRAME / Start position of Lead-out */ if (msf) { DSET24(&cp[8], istgt_lba2msf(spec->blockcnt)); } else { DSET24(&cp[8], spec->blockcnt); } plen = 11; len += plen; /* Track data */ cp = &data[hlen + len]; /* Session Number */ cp[0] = 1; /* ADR(7-4) CONTROL(3-0) */ cp[1] = 0x14; /* TNO */ cp[2] = 0; /* POINT */ cp[3] = 1; /* Min */ cp[4] = 0; /* Sec */ cp[5] = 0; /* Frame */ cp[6] = 0; /* Zero */ cp[7] = 0; /* PMIN / Start position of Lead-out */ /* PSEC / Start position of Lead-out */ /* PFRAME / Start position of Lead-out */ if (msf) { DSET24(&cp[8], istgt_lba2msf(0)); } else { DSET24(&cp[8], 0); } plen = 11; len += plen; /* TOC Data Length */ DSET16(&data[0], hlen + len - 2); break; default: ISTGT_ERRLOG("unsupported format 0x%x\n", format); return -1; } return hlen + len; } static int istgt_lu_dvd_scsi_read_disc_information(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int datatype, uint8_t *data) { int hlen = 0, len = 0; switch (datatype) { case 0x00: /* Disc Information Block */ /* Disc Information Length */ DSET16(&data[0], 0); hlen = 2; /* Disc Information Data Type(7-5) Erasable(4) */ /* State of last Session(3-2) Disc Status(1-0) */ BDSET8W(&data[2], datatype, 7, 3); BDADD8W(&data[2], 0, 4, 1); BDADD8W(&data[2], 0x03, 3, 2); /* Complete Session */ BDADD8W(&data[2], 0x02, 1, 2); /* Finalized Disc */ /* Number of First Track on Disc */ data[3] = 1; /* Number of Sessions (Least Significant Byte) */ data[4] = (1) & 0xff; /* First Track Number in Last Session (Least Significant Byte) */ data[5] = (1) & 0xff; /* Last Track Number in Last Session (Least Significant Byte) */ data[6] = (1) & 0xff; /* DID_V(7) DBC_V(6) URU(5) DAC_V(4) BG Format Status(1-0) */ BDSET8(&data[7], 0, 7); /* Disc ID Valid */ BDADD8(&data[7], 0, 6); /* Disc Bar Code Valid */ BDADD8(&data[7], 1, 5); /* Unrestricted Use Disc */ BDADD8(&data[7], 0, 4); /* Disc Application Code Valid */ BDADD8W(&data[7], 0, 1, 2); /* BG Format Status */ /* Disc Type */ data[8] = 0x00; /* CD-DA or CD-ROM Disc */ /* Number of Sessions (Most Significant Byte) */ data[9] = (1 >> 8) & 0xff; /* First Track Number in Last Session (Most Significant Byte) */ data[10] = (1 >> 8) & 0xff; /* Last Track Number in Last Session (Most Significant Byte) */ data[11] = (1 >> 8) & 0xff; /* Disc Identification */ DSET32(&data[12], 0); /* Last Session Lead-in Start Address */ DSET32(&data[16], 0); /* Last Possible Lead-out Start Address */ DSET32(&data[20], 0); /* Disc Bar Code */ memset(&data[24], 0, 8); /* Disc Application Code */ data[32] = 0; /* Number of OPC Tables */ data[33] = 0; /* OPC Table Entries */ //data[34] = 0; len = 34 - hlen; /* Disc Information Length */ DSET16(&data[0], len); break; case 0x01: /* Track Resources Information Block */ /* Disc Information Length */ DSET16(&data[0], 0); hlen = 2; /* Disc Information Data Type(7-5) */ BDSET8W(&data[2], datatype, 7, 3); /* Reserved */ data[3] = 0; /* Maximum possible number of the Tracks on the disc */ DSET16(&data[4], 99); /* Number of the assigned Tracks on the disc */ DSET16(&data[6], 1); /* Maximum possible number of appendable Tracks on the disc */ DSET16(&data[8], 99); /* Current number of appendable Tracks on the disc */ DSET16(&data[10], 99); len = 12 - hlen; /* Disc Information Length */ DSET16(&data[0], len); break; case 0x02: /* POW Resources Information Block */ /* Disc Information Length */ DSET16(&data[0], 0); hlen = 2; /* Disc Information Data Type(7-5) */ BDSET8W(&data[2], datatype, 7, 3); /* Reserved */ data[3] = 0; /* Remaining POW Replacements */ DSET32(&data[4], 0); /* Remaining POW Reallocation Map Entries */ DSET32(&data[8], 0); /* Number of Remaining POW Updates */ DSET32(&data[12], 0); len = 16 - hlen; /* Disc Information Length */ DSET16(&data[0], len); break; default: ISTGT_ERRLOG("unsupported datatype 0x%x\n", datatype); return -1; } return hlen + len; } static int istgt_lu_dvd_scsi_read_disc_structure(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int mediatype, int layernumber, int format, int agid, uint8_t *data) { uint8_t *cp; int hlen = 0, len = 0; if (mediatype == 0x00) { /* DVD and HD DVD types */ } else if (mediatype == 0x01) { /* BD */ } else { /* Reserved */ } switch (format) { case 0x00: /* Physical Format Information */ /* Disc Structure Data Length */ DSET16(&data[0], 0); /* Reserved */ data[2] = 0; /* Reserved */ data[3] = 0; hlen = 4; /* Physical Format Information */ cp = &data[hlen + len]; /* Disk Category(7-4) Part Version(3-0) */ BDSET8W(&cp[0], 0x00, 7, 4); /* DVD-ROM */ BDADD8W(&cp[0], 0x01, 3, 4); /* part 1 */ /* Disc Size(7-4) Maximum Rate(0-3) */ BDSET8W(&cp[1], 0x00, 7, 4); /* 120mm */ BDADD8W(&cp[1], 0x0f, 3, 4); /* Not Specified */ /* Number of Layers(6-5) Track(4) Layer Type(3-0) */ BDSET8W(&cp[2], 0x00, 6, 2); /* one layer */ BDADD8W(&cp[2], 0x00, 4, 1); /* Parallel Track Path */ BDADD8W(&cp[2], 0x00, 3, 4); /* embossed data */ /* Linear Density(7-4) Track Density(3-0) */ BDSET8W(&cp[3], 0x00, 7, 4); /* 0.267 um/bit */ BDADD8W(&cp[3], 0x00, 3, 4); /* 0.74 um/track */ /* Starting Physical Sector Number of Data Area */ DSET32(&cp[4], 0); /* End Physical Sector Number of Data Area */ DSET32(&cp[8], spec->blockcnt - 1); /* End Physical Sector Number in Layer 0 */ DSET32(&cp[12], spec->blockcnt - 1); /* BCA(7) */ BDSET8(&cp[16], 0, 7); /* Media Specific */ memset(&cp[17], 0, 2048 - 16); len = 2048; /* Disc Information Length */ DSET16(&data[0], hlen + len - 2); break; case 0x01: /* DVD Copyright Information */ /* Disc Structure Data Length */ DSET16(&data[0], 0); /* Reserved */ data[2] = 0; /* Reserved */ data[3] = 0; hlen = 4; /* DVD Copyright Information */ cp = &data[hlen + len]; /* Copyright Protection System Type */ cp[0] = 0x00; //cp[0] = 0x01; /* CSS/CPPM */ /* Region Management Information */ cp[1] = 0x00; //cp[1] = 0xff & ~(1 << (2 - 1)); /* 2=Japan */ /* Reserved */ cp[2] = 0; /* Reserved */ cp[3] = 0; len = 4; /* Disc Information Length */ DSET16(&data[0], hlen + len - 2); break; default: ISTGT_ERRLOG("unsupported format 0x%x\n", format); return -1; } return hlen + len; } static int istgt_lu_dvd_scsi_report_key(ISTGT_LU_DVD *spec, CONN_Ptr conn, uint8_t *cdb, int keyclass, int agid, int keyformat, uint8_t *data) { uint8_t *cp; int hlen = 0, len = 0; if (keyclass == 0x00) { /* DVD CSS/CPPM or CPRM */ } else { return -1; } switch (keyformat) { case 0x08: /* Report Drive region settings */ /* REPORT KEY Data Length */ DSET16(&data[0], 6); /* Reserved */ data[2] = 0; /* Reserved */ data[3] = 0; hlen = 4; /* RPC State */ cp = &data[hlen + len]; /* Type Code(7-6) # of Vendor Resets Available(5-3) */ /* # of User Controlled Changes Available(2-0) */ BDSET8W(&cp[0], 0x00, 7, 2); /* No Drive region setting */ //BDSET8W(&cp[0], 0x01, 7, 2); /* Drive region is set */ BDADD8W(&cp[0], 4, 5, 3); /* # of vendor */ BDADD8W(&cp[0], 5, 2, 3); /* # of user */ /* Region Mask */ cp[1] = 0; //cp[1] = 0xff & ~(1 << (2 - 1)); /* 2=Japan */ /* RPC Scheme */ cp[2] = 0; //cp[2] = 0x01; /* RPC Phase II */ /* Reserved */ cp[3] = 0; len = 4; /* REPORT KEY Data Length */ DSET16(&data[0], hlen + len - 2); break; case 0x00: /* AGID for CSS/CPPM */ case 0x01: /* Challenge Key */ case 0x02: /* KEY1 */ case 0x04: /* TITLE KEY */ case 0x05: /* ASF */ case 0x11: /* AGID for CPRM */ /* not supported */ return -1; default: ISTGT_ERRLOG("unsupported keyformat 0x%x\n", keyformat); return -1; } return hlen + len; } static int istgt_lu_dvd_lbread(ISTGT_LU_DVD *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint8_t *data; uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; int64_t rc; if (len == 0) { lu_cmd->data = NULL; lu_cmd->data_len = 0; return 0; } maxlba = spec->blockcnt; llen = (uint64_t) len; blen = spec->blocklen; offset = lba * blen; nbytes = llen * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Read: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } if (nbytes > lu_cmd->iobufsize) { ISTGT_ERRLOG("nbytes(%u) > iobufsize(%u)\n", nbytes, lu_cmd->iobufsize); return -1; } data = lu_cmd->iobuf; rc = istgt_lu_dvd_seek(spec, offset); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_seek() failed\n"); return -1; } rc = istgt_lu_dvd_read(spec, data, nbytes); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_read() failed\n"); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Read %"PRId64"/%"PRIu64" bytes\n", rc, nbytes); lu_cmd->data = data; lu_cmd->data_len = rc; return 0; } #if 0 static int istgt_lu_dvd_lbwrite(ISTGT_LU_DVD *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint8_t *data; uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; int64_t rc; if (len == 0) { lu_cmd->data_len = 0; return 0; } maxlba = spec->blockcnt; llen = (uint64_t) len; blen = spec->blocklen; offset = lba * blen; nbytes = llen * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Write: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } if (nbytes > lu_cmd->iobufsize) { ISTGT_ERRLOG("nbytes(%u) > iobufsize(%u)\n", nbytes, lu_cmd->iobufsize); return -1; } data = lu_cmd->iobuf; rc = istgt_lu_dvd_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, nbytes); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_transfer_data() failed\n"); return -1; } if (spec->lu->readonly) { ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num); return -1; } if (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY) { ISTGT_ERRLOG("LU%d: readonly media\n", spec->lu->num); return -1; } rc = istgt_lu_dvd_seek(spec, offset); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_seek() failed\n"); return -1; } rc = istgt_lu_dvd_write(spec, data, nbytes); if (rc < 0 || rc != nbytes) { ISTGT_ERRLOG("lu_dvd_write() failed\n"); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n", rc, nbytes); lu_cmd->data_len = rc; return 0; } static int istgt_lu_dvd_lbsync(ISTGT_LU_DVD *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; int64_t rc; if (len == 0) { return 0; } maxlba = spec->blockcnt; llen = (uint64_t) len; blen = spec->blocklen; offset = lba * blen; nbytes = llen * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Sync: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } rc = istgt_lu_dvd_sync(spec, offset, nbytes); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_sync() failed\n"); return -1; } return 0; } #endif static int istgt_lu_dvd_build_sense_data(ISTGT_LU_DVD *spec, uint8_t *data, int sk, int asc, int ascq) { int rc; rc = istgt_lu_scsi_build_sense_data(data, sk, asc, ascq); if (rc < 0) { return -1; } return rc; } static int istgt_lu_dvd_build_sense_media(ISTGT_LU_DVD *spec, uint8_t *data) { uint8_t *sense_data; int *sense_len; int data_len; sense_data = data; sense_len = &data_len; *sense_len = 0; if (!spec->mload && !spec->mchanged) { /* MEDIUM NOT PRESENT */ BUILD_SENSE(NOT_READY, 0x3a, 0x00); return data_len; } if (spec->mchanged) { /* MEDIUM NOT PRESENT */ BUILD_SENSE(NOT_READY, 0x3a, 0x00); return data_len; #if 0 /* LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE */ BUILD_SENSE(NOT_READY, 0x04, 0x00); return data_len; /* LOGICAL UNIT IS IN PROCESS OF BECOMING READY */ BUILD_SENSE(NOT_READY, 0x04, 0x01); return data_len; #endif } return 0; } int istgt_lu_dvd_reset(ISTGT_LU_Ptr lu, int lun) { ISTGT_LU_DVD *spec; int flags; int rc; if (lu == NULL) { return -1; } if (lun >= lu->maxlun) { return -1; } if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) { return -1; } if (lu->lun[lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { return -1; } spec = (ISTGT_LU_DVD *) lu->lun[lun].spec; if (spec->lock) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "unlock by reset\n"); spec->lock = 0; } /* re-open file */ if (!spec->lu->readonly && !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { rc = istgt_lu_dvd_sync(spec, 0, spec->size); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_dvd_sync() failed\n", lu->num, lun); /* ignore error */ } } rc = istgt_lu_dvd_close(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_dvd_close() failed\n", lu->num, lun); /* ignore error */ } flags = (lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) ? O_RDONLY : O_RDWR; rc = istgt_lu_dvd_open(spec, flags, 0666); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_dvd_open() failed\n", lu->num, lun); return -1; } return 0; } int istgt_lu_dvd_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_Ptr lu; ISTGT_LU_DVD *spec; uint8_t *data; uint8_t *cdb; uint64_t fmt_lun; uint64_t lun; uint64_t method; uint32_t allocation_len; int data_len; int data_alloc_len; uint64_t lba; uint32_t transfer_len; uint8_t *sense_data; int *sense_len; int rc; if (lu_cmd == NULL) return -1; lu = lu_cmd->lu; if (lu == NULL) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } spec = NULL; cdb = lu_cmd->cdb; data = lu_cmd->data; data_alloc_len = lu_cmd->alloc_len; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; fmt_lun = lu_cmd->lun; method = (fmt_lun >> 62) & 0x03U; fmt_lun = fmt_lun >> 48; if (method == 0x00U) { lun = fmt_lun & 0x00ffU; } else if (method == 0x01U) { lun = fmt_lun & 0x3fffU; } else { lun = 0xffffU; } if (lun >= lu->maxlun) { #ifdef ISTGT_TRACE_DVD ISTGT_ERRLOG("LU%d: LUN%4.4"PRIx64" invalid\n", lu->num, lun); #endif /* ISTGT_TRACE_DVD */ if (cdb[0] == SPC_INQUIRY) { allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], 0x03, 7, 3); BDADD8W(&data[0], 0x1f, 4, 5); data_len = 96; memset(&data[1], 0, data_len - 1); /* ADDITIONAL LENGTH */ data[4] = data_len - 5; lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } else { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } } spec = (ISTGT_LU_DVD *) lu->lun[lun].spec; if (spec == NULL) { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64"\n", cdb[0], lu_cmd->lun); #ifdef ISTGT_TRACE_DVD if (cdb[0] != SPC_TEST_UNIT_READY && cdb[0] != MMC_GET_EVENT_STATUS_NOTIFICATION) { istgt_scsi_dump_cdb(cdb); } else { istgt_scsi_dump_cdb(cdb); } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "mload=%d, mchanged=%d, mwait=%d\n", spec->mload, spec->mchanged, spec->mwait); #endif /* ISTGT_TRACE_DVD */ switch (cdb[0]) { case SPC_INQUIRY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "INQUIRY\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_inquiry(spec, conn, cdb, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "INQUIRY", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC_REPORT_LUNS: { int sel; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT LUNS\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); return -1; } sel = cdb[2]; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sel=%x\n", sel); allocation_len = DGET32(&cdb[6]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (allocation_len < 16) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_report_luns(lu, conn, cdb, sel, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "REPORT LUNS", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; } break; case SPC_TEST_UNIT_READY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "TEST_UNIT_READY\n"); { data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); /* media state change? */ if (spec->mchanged) { /* wait OS polling */ if (spec->mwait > 0) { spec->mwait--; } else { /* load new media */ spec->mchanged = 0; spec->mload = 1; } } if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* OK media present */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_START_STOP_UNIT: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "START_STOP_UNIT\n"); { int pc, fl, loej, start; pc = BGET8W(&cdb[4], 7, 4); fl = BGET8(&cdb[4], 2); loej = BGET8(&cdb[4], 1); start = BGET8(&cdb[4], 0); data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (!loej) { if (start) { /* start */ } else { /* stop */ } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } /* loej=1 */ if (start) { /* load disc */ if (!spec->mload) { if (istgt_lu_dvd_load_media(spec) < 0) { ISTGT_ERRLOG("lu_dvd_load_media() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* OK load */ } } else { /* eject */ if (!spec->lock) { if (spec->mload) { if (istgt_lu_dvd_unload_media(spec) < 0) { ISTGT_ERRLOG("lu_dvd_unload_media() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* OK unload */ } } else { /* MEDIUM REMOVAL PREVENTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x53, 0x02); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_PREVENT_ALLOW_MEDIUM_REMOVAL: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PREVENT_ALLOW_MEDIUM_REMOVAL\n"); { int persistent, prevent; persistent = BGET8(&cdb[4], 1); prevent = BGET8(&cdb[4], 0); data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (persistent) { if (prevent) { /* Persistent Prevent */ } else { /* Persistent Allow */ } } else { if (prevent) { /* Locked */ spec->lock = 1; } else { /* Unlocked */ spec->lock = 0; } } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_READ_CAPACITY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_CAPACITY\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); return -1; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (spec->blockcnt - 1 > 0xffffffffULL) { DSET32(&data[0], 0xffffffffUL); } else { DSET32(&data[0], (uint32_t) (spec->blockcnt - 1)); } DSET32(&data[4], (uint32_t) spec->blocklen); data_len = 8; lu_cmd->data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC_MODE_SELECT_6: { int pf, sp, pllen; int mdlen, mt, dsp, bdlen; pf = BGET8(&cdb[1], 4); sp = BGET8(&cdb[1], 0); pllen = cdb[4]; /* Parameter List Length */ /* Data-Out */ rc = istgt_lu_dvd_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, pllen); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SELECT(6)", lu_cmd->iobuf, pllen); #endif data = lu_cmd->iobuf; mdlen = data[0]; /* Mode Data Length */ mt = data[1]; /* Medium Type */ dsp = data[2]; /* Device-Specific Parameter */ bdlen = data[3]; /* Block Descriptor Length */ /* Short LBA mode parameter block descriptor */ /* data[4]-data[7] Number of Blocks */ /* data[8]-data[11] Block Length */ /* page data */ data_len = istgt_lu_dvd_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[4 + bdlen], pllen - (4 + bdlen)); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = pllen; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SELECT_10: { int pf, sp, pllen; int mdlen, mt, dsp, bdlen; int llba; pf = BGET8(&cdb[1], 4); sp = BGET8(&cdb[1], 0); pllen = DGET16(&cdb[7]); /* Parameter List Length */ /* Data-Out */ rc = istgt_lu_dvd_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, pllen); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SELECT(10)", lu_cmd->iobuf, pllen); #endif data = lu_cmd->iobuf; mdlen = DGET16(&data[0]); /* Mode Data Length */ mt = data[2]; /* Medium Type */ dsp = data[3]; /* Device-Specific Parameter */ llba = BGET8(&data[4], 0); /* Long LBA */ bdlen = DGET16(&data[6]); /* Block Descriptor Length */ if (llba) { /* Long LBA mode parameter block descriptor */ /* data[8]-data[15] Number of Blocks */ /* data[16]-data[19] Reserved */ /* data[20]-data[23] Block Length */ } else { /* Short LBA mode parameter block descriptor */ /* data[8]-data[11] Number of Blocks */ /* data[12]-data[15] Block Length */ } /* page data */ data_len = istgt_lu_dvd_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[8 + bdlen], pllen - (8 + bdlen)); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = pllen; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SENSE_6: { int dbd, pc, page, subpage; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dbd = BGET8(&cdb[1], 3); pc = BGET8W(&cdb[2], 7, 2); page = BGET8W(&cdb[2], 5, 6); subpage = cdb[3]; allocation_len = cdb[4]; if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_mode_sense6(spec, conn, cdb, dbd, pc, page, subpage, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SENSE(6)", data, data_len); #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SENSE_10: { int dbd, pc, page, subpage; int llbaa; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } llbaa = BGET8(&cdb[1], 4); dbd = BGET8(&cdb[1], 3); pc = BGET8W(&cdb[2], 7, 2); page = BGET8W(&cdb[2], 5, 6); subpage = cdb[3]; allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_mode_sense10(spec, conn, cdb, llbaa, dbd, pc, page, subpage, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SENSE(10)", data, data_len); #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } #if 0 case SPC_LOG_SELECT: case SPC_LOG_SENSE: /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; #endif case SPC_REQUEST_SENSE: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REQUEST_SENSE\n"); { int desc; int sk, asc, ascq; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } desc = BGET8(&cdb[1], 0); if (desc != 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); /* media state change? */ if (spec->mchanged) { /* wait OS polling */ if (spec->mwait > 0) { spec->mwait--; } else { /* load new media */ spec->mchanged = 0; spec->mload = 1; } } if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } allocation_len = cdb[4]; if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); if (!spec->sense) { /* NO ADDITIONAL SENSE INFORMATION */ sk = ISTGT_SCSI_SENSE_NO_SENSE; asc = 0x00; ascq = 0x00; } else { sk = (spec->sense >> 16) & 0xffU; asc = (spec->sense >> 8) & 0xffU; ascq = spec->sense & 0xffU; } data_len = istgt_lu_dvd_build_sense_data(spec, sense_data, sk, asc, ascq); if (data_len < 0 || data_len < 2) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* omit SenseLength */ data_len -= 2; memcpy(data, sense_data + 2, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_GET_CONFIGURATION: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "GET_CONFIGURATION\n"); { int rt, sfn; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } rt = BGET8W(&cdb[1], 1, 2); sfn = DGET16(&cdb[2]); data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_get_configuration(spec, conn, cdb, rt, sfn, data); if (data_len < 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_GET_EVENT_STATUS_NOTIFICATION: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "GET_EVENT_STATUS_NOTIFICATION\n"); { int polled, ncr; int keep = 0; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } polled = BGET8(&cdb[1], 0); ncr = cdb[4]; allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); if (!polled) { /* asynchronous operation */ /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (allocation_len <= 4) { /* shall not clear any event */ keep = 1; } data_len = istgt_lu_dvd_scsi_get_event_status_notification(spec, conn, cdb, keep, ncr, data); if (data_len < 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "EVENT", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_GET_PERFORMANCE: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "GET_PERFORMANCE\n"); { int dt, mnd, type; data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } dt = BGET8W(&cdb[1], 4, 5); lba = DGET32(&cdb[2]); mnd = DGET16(&cdb[8]); type = cdb[10]; /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } case MMC_MECHANISM_STATUS: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MECHANISM_STATUS\n"); { if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } allocation_len = DGET16(&cdb[8]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_mechanism_status(spec, conn, cdb, data); if (data_len < 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_READ_TOC_PMA_ATIP: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_TOC_PMA_ATIP\n"); { int msf, format, track; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } msf = BGET8(&cdb[1], 1); format = BGET8W(&cdb[2], 3, 4); track = cdb[6]; allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_read_toc(spec, conn, cdb, msf, format, track, data); if (data_len < 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_READ_DISC_INFORMATION: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_DISC_INFORMATION\n"); { int datatype; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } datatype = BGET8W(&cdb[1], 2, 3); allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_read_disc_information(spec, conn, cdb, datatype, data); if (data_len < 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_READ_DISC_STRUCTURE: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_DISC_STRUCTURE\n"); { int mediatype, layernumber, format, agid; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } mediatype = BGET8W(&cdb[1], 3, 4); layernumber = cdb[6]; format = cdb[7]; agid = BGET8W(&cdb[10], 7, 2); allocation_len = DGET16(&cdb[8]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_read_disc_structure(spec, conn, cdb, mediatype, layernumber, format, agid, data); if (data_len < 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_READ_SUB_CHANNEL: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_SUB_CHANNEL\n"); { data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } case MMC_REPORT_KEY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT_KEY\n"); { int keyclass, agid, keyformat; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } keyclass = cdb[7]; agid = BGET8W(&cdb[10], 7, 2); keyformat = BGET8W(&cdb[10], 5, 6); allocation_len = DGET16(&cdb[8]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_dvd_scsi_report_key(spec, conn, cdb, keyclass, agid, keyformat, data); if (data_len < 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_SEND_KEY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SEND_KEY\n"); { data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } case MMC_READ_10: { int dpo, fua; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); lba = (uint64_t) DGET32(&cdb[2]); transfer_len = (uint32_t) DGET16(&cdb[7]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_10(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_dvd_lbread(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_lbread() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case MMC_READ_12: { int dpo, fua; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_dvd_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); lba = (uint64_t) DGET32(&cdb[2]); transfer_len = (uint32_t) DGET32(&cdb[6]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_12(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_dvd_lbread(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_dvd_lbread() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } #if 0 case MMC_WRITE_10: case MMC_WRITE_AND_VERIFY_10: case MMC_WRITE_12: case MMC_VERIFY_10: case MMC_SYNCHRONIZE_CACHE: /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; #endif /* XXX TODO: fix */ case SPC2_RELEASE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_6\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RELEASE_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_10\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RESERVE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_6\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RESERVE_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_10\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; default: ISTGT_ERRLOG("unsupported SCSI OP=0x%x\n", cdb[0]); /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64" status=0x%x," " complete\n", cdb[0], lu_cmd->lun, lu_cmd->status); return 0; } istgt-20111008/src/istgt_log.c000644 000000 000000 00000015045 11317704406 016137 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "istgt.h" #include "istgt_log.h" #include "istgt_misc.h" //static int g_trace_flag = 0; int g_trace_flag = 0; int g_warn_flag = 1; static int g_log_facility = ISTGT_LOG_FACILITY; static int g_log_priority = ISTGT_LOG_PRIORITY; int istgt_set_log_facility(const char *facility) { if (strcasecmp(facility, "daemon") == 0) { g_log_facility = LOG_DAEMON; } else if (strcasecmp(facility, "auth") == 0) { g_log_facility = LOG_AUTH; } else if (strcasecmp(facility, "authpriv") == 0) { g_log_facility = LOG_AUTHPRIV; } else if (strcasecmp(facility, "local1") == 0) { g_log_facility = LOG_LOCAL1; } else if (strcasecmp(facility, "local2") == 0) { g_log_facility = LOG_LOCAL2; } else if (strcasecmp(facility, "local3") == 0) { g_log_facility = LOG_LOCAL3; } else if (strcasecmp(facility, "local4") == 0) { g_log_facility = LOG_LOCAL4; } else if (strcasecmp(facility, "local5") == 0) { g_log_facility = LOG_LOCAL5; } else if (strcasecmp(facility, "local6") == 0) { g_log_facility = LOG_LOCAL6; } else if (strcasecmp(facility, "local7") == 0) { g_log_facility = LOG_LOCAL7; } else { g_log_facility = ISTGT_LOG_FACILITY; return -1; } return 0; } int istgt_set_log_priority(const char *priority) { if (strcasecmp(priority, "emerg") == 0) { g_log_priority = LOG_EMERG; } else if (strcasecmp(priority, "alert") == 0) { g_log_priority = LOG_ALERT; } else if (strcasecmp(priority, "crit") == 0) { g_log_priority = LOG_CRIT; } else if (strcasecmp(priority, "err") == 0) { g_log_priority = LOG_ERR; } else if (strcasecmp(priority, "warning") == 0) { g_log_priority = LOG_WARNING; } else if (strcasecmp(priority, "notice") == 0) { g_log_priority = LOG_NOTICE; } else if (strcasecmp(priority, "info") == 0) { g_log_priority = LOG_INFO; } else if (strcasecmp(priority, "debug") == 0) { g_log_priority = LOG_DEBUG; } else { g_log_priority = ISTGT_LOG_PRIORITY; return -1; } return 0; } void istgt_log(const char *file, const int line, const char *func, const char *format, ...) { char buf[MAX_TMPBUF]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof buf, format, ap); if (file != NULL) { if (func != NULL) { fprintf(stderr, "%s:%4d:%s: %s", file, line, func, buf); syslog(g_log_priority, "%s:%4d:%s: %s", file, line, func, buf); } else { fprintf(stderr, "%s:%4d: %s", file, line, buf); syslog(g_log_priority, "%s:%4d: %s", file, line, buf); } } else { fprintf(stderr, "%s", buf); syslog(g_log_priority, "%s", buf); } va_end(ap); } void istgt_noticelog(const char *file, const int line, const char *func, const char *format, ...) { char buf[MAX_TMPBUF]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof buf, format, ap); if (file != NULL) { if (func != NULL) { fprintf(stderr, "%s:%4d:%s: %s", file, line, func, buf); syslog(LOG_NOTICE, "%s:%4d:%s: %s", file, line, func, buf); } else { fprintf(stderr, "%s:%4d: %s", file, line, buf); syslog(LOG_NOTICE, "%s:%4d: %s", file, line, buf); } } else { fprintf(stderr, "%s", buf); syslog(LOG_NOTICE, "%s", buf); } va_end(ap); } void istgt_tracelog(const int flag, const char *file, const int line, const char *func, const char *format, ...) { char buf[MAX_TMPBUF]; va_list ap; va_start(ap, format); if (g_trace_flag & flag) { vsnprintf(buf, sizeof buf, format, ap); if (func != NULL) { fprintf(stderr, "%s:%4d:%s: %s", file, line, func, buf); //syslog(LOG_INFO, "%s:%4d:%s: %s", file, line, func, buf); } else { fprintf(stderr, "%s:%4d: %s", file, line, buf); //syslog(LOG_INFO, "%s:%4d: %s", file, line, buf); } } va_end(ap); } void istgt_errlog(const char *file, const int line, const char *func, const char *format, ...) { char buf[MAX_TMPBUF]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof buf, format, ap); if (func != NULL) { fprintf(stderr, "%s:%4d:%s: ***ERROR*** %s", file, line, func, buf); syslog(LOG_ERR, "%s:%4d:%s: ***ERROR*** %s", file, line, func, buf); } else { fprintf(stderr, "%s:%4d: ***ERROR*** %s", file, line, buf); syslog(LOG_ERR, "%s:%4d: ***ERROR*** %s", file, line, buf); } va_end(ap); } void istgt_warnlog(const char *file, const int line, const char *func, const char *format, ...) { char buf[MAX_TMPBUF]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof buf, format, ap); if (func != NULL) { fprintf(stderr, "%s:%4d:%s: ***WARNING*** %s", file, line, func, buf); syslog(LOG_WARNING, "%s:%4d:%s: ***WARNING*** %s", file, line, func, buf); } else { fprintf(stderr, "%s:%4d: ***WARNING*** %s", file, line, buf); syslog(LOG_WARNING, "%s:%4d: ***WARNING*** %s", file, line, buf); } va_end(ap); } void istgt_open_log(void) { if (g_log_facility != 0) { openlog("istgt", LOG_PID, g_log_facility); } else { openlog("istgt", LOG_PID, ISTGT_LOG_FACILITY); } } void istgt_close_log(void) { closelog(); } void istgt_set_trace_flag(int flag) { if (flag == ISTGT_TRACE_NONE) { g_trace_flag = 0; } else { g_trace_flag |= flag; } } void istgt_trace_dump(int flag, const char *label, const uint8_t *buf, size_t len) { if (g_trace_flag & flag) { istgt_fdump(stderr, label, buf, len); } } istgt-20111008/src/build.h.in000644 000000 000000 00000003004 11627501424 015644 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef BUILD_H #define BUILD_H #define BUILD_PREFIX "@prefix@" #define BUILD_ETC_ISTGT "@configdir@" #define BUILD_VAR_ISTGT "@mediadir@" #endif /* BUILD_H */ istgt-20111008/src/istgt_queue.c000644 000000 000000 00000006672 11317704406 016510 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "istgt_misc.h" #include "istgt_queue.h" int istgt_queue_init(ISTGT_QUEUE_Ptr head) { if (head == NULL) return -1; head->prev = NULL; head->next = NULL; head->elem = NULL; return 0; } void istgt_queue_destroy(ISTGT_QUEUE_Ptr head) { ISTGT_QUEUE_Ptr qp; ISTGT_QUEUE_Ptr next; if (head == NULL) return; for (qp = head->next; qp != NULL && qp != head; qp = next) { next = qp->next; free(qp); } head->next = NULL; head->prev = NULL; } int istgt_queue_count(ISTGT_QUEUE_Ptr head) { ISTGT_QUEUE_Ptr qp; int num; if (head == NULL) return 0; num = 0; for (qp = head->next; qp != NULL && qp != head; qp = qp->next) { num++; } return num; } int istgt_queue_enqueue(ISTGT_QUEUE_Ptr head, void *elem) { ISTGT_QUEUE_Ptr qp; ISTGT_QUEUE_Ptr tail; if (head == NULL) return -1; qp = xmalloc(sizeof *qp); memset(qp, 0, sizeof *qp); qp->elem = elem; tail = head->prev; if (tail == NULL) { head->next = qp; head->prev = qp; qp->next = head; qp->prev = head; } else { tail->next = qp; head->prev = qp; qp->next = head; qp->prev = tail; } return 0; } void * istgt_queue_dequeue(ISTGT_QUEUE_Ptr head) { ISTGT_QUEUE_Ptr first; ISTGT_QUEUE_Ptr next; void *elem; if (head == NULL) return NULL; first = head->next; if (first == NULL || first == head) { return NULL; } else { elem = first->elem; next = first->next; xfree(first); if (next == NULL) { head->next = NULL; head->prev = NULL; } else { head->next = next; next->prev = head; } } return elem; } int istgt_queue_enqueue_first(ISTGT_QUEUE_Ptr head, void *elem) { ISTGT_QUEUE_Ptr qp; ISTGT_QUEUE_Ptr first; if (head == NULL) return -1; qp = xmalloc(sizeof *qp); memset(qp, 0, sizeof *qp); qp->elem = elem; first = head->next; if (first == NULL || first == head) { head->next = qp; head->prev = qp; qp->next = head; qp->prev = head; } else { head->next = qp; first->prev = qp; qp->next = first; qp->prev = head; } return 0; } istgt-20111008/src/istgt_ver.h000644 000000 000000 00000002750 11643630071 016154 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_VER_H #define ISTGT_VER_H #define ISTGT_VERSION "0.4" #define ISTGT_EXTRA_VERSION "20111008" #endif /* ISTGT_VER_H */ istgt-20111008/src/istgt_conf.c000644 000000 000000 00000022244 11317704406 016302 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "istgt.h" #include "istgt_misc.h" #include "istgt_conf.h" //#define CF_DELIM " \t,;" #define CF_DELIM " \t" static void istgt_free_all_cf_section(CF_SECTION *sp); static void istgt_free_all_cf_item(CF_ITEM *ip); static void istgt_free_all_cf_value(CF_VALUE *vp); CONFIG * istgt_allocate_config(void) { CONFIG *cp; cp = xmalloc(sizeof *cp); memset(cp, 0, sizeof *cp); cp->file = NULL; cp->section = NULL; return cp; } void istgt_free_config(CONFIG *cp) { if (cp == NULL) return; if (cp->section != NULL) { istgt_free_all_cf_section(cp->section); } xfree(cp->file); xfree(cp); } static CF_SECTION * istgt_allocate_cf_section(void) { CF_SECTION *sp; sp = xmalloc(sizeof *sp); memset(sp, 0, sizeof *sp); sp->next = NULL; sp->item = NULL; return sp; } static void istgt_free_cf_section(CF_SECTION *sp) { if (sp == NULL) return; if (sp->item) { istgt_free_all_cf_item(sp->item); } xfree(sp->name); xfree(sp); } static void istgt_free_all_cf_section(CF_SECTION *sp) { CF_SECTION *next; if (sp == NULL) return; while (sp != NULL) { next = sp->next; istgt_free_cf_section(sp); sp = next; } } static CF_ITEM * istgt_allocate_cf_item(void) { CF_ITEM *ip; ip = xmalloc(sizeof *ip); memset(ip, 0, sizeof *ip); ip->next = NULL; ip->key = NULL; ip->val = NULL; return ip; } static void istgt_free_cf_item(CF_ITEM *ip) { if (ip == NULL) return; if (ip->val != NULL) { istgt_free_all_cf_value(ip->val); } xfree(ip->key); xfree(ip); } static void istgt_free_all_cf_item(CF_ITEM *ip) { CF_ITEM *next; if (ip == NULL) return; while (ip != NULL) { next = ip->next; istgt_free_cf_item(ip); ip = next; } } static CF_VALUE * istgt_allocate_cf_value(void) { CF_VALUE *vp; vp = xmalloc(sizeof *vp); memset(vp, 0, sizeof *vp); vp->next = NULL; vp->value = NULL; return vp; } static void istgt_free_cf_value(CF_VALUE *vp) { if (vp == NULL) return; xfree(vp->value); xfree(vp); } static void istgt_free_all_cf_value(CF_VALUE *vp) { CF_VALUE *next; if (vp == NULL) return; while (vp != NULL) { next = vp->next; istgt_free_cf_value(vp); vp = next; } } CF_SECTION * istgt_find_cf_section(CONFIG *cp, const char *name) { CF_SECTION *sp; if (name == NULL || name[0] == '\0') return NULL; for (sp = cp->section; sp != NULL; sp = sp->next) { if (sp->name != NULL && sp->name[0] == name[0] && strcasecmp(sp->name, name) == 0) { return sp; } } return NULL; } static void istgt_append_cf_section(CONFIG *cp, CF_SECTION *sp) { CF_SECTION *last; if (cp == NULL) return; if (cp->section == NULL) { cp->section = sp; return; } for (last = cp->section; last->next != NULL; last = last->next) ; last->next = sp; } CF_ITEM * istgt_find_cf_nitem(CF_SECTION *sp, const char *key, int idx) { CF_ITEM *ip; int i; if (key == NULL || key[0] == '\0') return NULL; i = 0; for (ip = sp->item; ip != NULL; ip = ip->next) { if (ip->key != NULL && ip->key[0] == key[0] && strcasecmp(ip->key, key) == 0) { if (i == idx) { return ip; } i++; } } return NULL; } CF_ITEM * istgt_find_cf_item(CF_SECTION *sp, const char *key) { return istgt_find_cf_nitem(sp, key, 0); } static void istgt_append_cf_item(CF_SECTION *sp, CF_ITEM *ip) { CF_ITEM *last; if (sp == NULL) return; if (sp->item == NULL) { sp->item = ip; return; } for (last = sp->item; last->next != NULL; last = last->next) ; last->next = ip; } static void istgt_append_cf_value(CF_ITEM *ip, CF_VALUE *vp) { CF_VALUE *last; if (ip == NULL) return; if (ip->val == NULL) { ip->val = vp; return; } for (last = ip->val; last->next != NULL; last = last->next) ; last->next = vp; } static void istgt_set_cf_section_type(CF_SECTION *sp) { static struct cfst_table_t { const char *name; CF_SECTION_TYPE type; } cfst_table[] = { { "Global", ST_GLOBAL }, { "UnitControl", ST_UNITCONTROL }, { "PortalGroup", ST_PORTALGROUP }, { "InitiatorGroup", ST_INITIATORGROUP }, { "LogicalUnit", ST_LOGICAL_UNIT }, { "AuthGroup", ST_AUTHGROUP }, { NULL, ST_INVALID }, }; int i; if (sp == NULL || sp->name == NULL) return; for (i = 0; cfst_table[i].name != NULL; i++) { if (sp->name[0] == cfst_table[i].name[0] && strncasecmp(sp->name, cfst_table[i].name, strlen(cfst_table[i].name)) == 0) { sp->type = cfst_table[i].type; return; } } sp->type = ST_NONE; } static int parse_line(CONFIG *cp, char *lp) { CF_SECTION *sp; CF_ITEM *ip; CF_VALUE *vp; char *arg; char *key; char *val; char *p; int num; arg = trim_string(lp); if (arg[0] == '[') { /* section */ arg++; key = strsepq(&arg, "]"); if (key == NULL || arg != NULL) { fprintf(stderr, "broken section\n"); return -1; } /* determine section number */ for (p = key; *p != '\0' && !isdigit((int) *p); p++) ; if (*p != '\0') { num = (int)strtol(p, NULL, 10); } else { num = 0; } sp = istgt_find_cf_section(cp, key); if (sp == NULL) { sp = istgt_allocate_cf_section(); istgt_append_cf_section(cp, sp); } cp->current_section = sp; sp->name = xstrdup(key); sp->num = num; istgt_set_cf_section_type(sp); } else { /* parameters */ sp = cp->current_section; if (sp == NULL) { fprintf(stderr, "unknown section\n"); return -1; } key = strsepq(&arg, CF_DELIM); if (key == NULL) { fprintf(stderr, "broken key\n"); return -1; } ip = istgt_allocate_cf_item(); istgt_append_cf_item(sp, ip); ip->key = xstrdup(key); ip->val = NULL; if (arg != NULL) { /* key has value(s) */ while (arg != NULL) { val = strsepq(&arg, CF_DELIM); vp = istgt_allocate_cf_value(); istgt_append_cf_value(ip, vp); vp->value = xstrdup(val); } } } return 0; } static char * fgets_line (FILE *fp) { char *dst, *p; size_t total, len; dst = p = xmalloc(MAX_TMPBUF); dst[0] = '\0'; total = 0; while (fgets(p, MAX_TMPBUF, fp) != NULL) { len = strlen(p); total += len; if (len + 1 < MAX_TMPBUF || dst[total - 1] == '\n') { return xrealloc(dst, total + 1); } dst = xrealloc (dst, total + MAX_TMPBUF); p = dst + total; } if (feof(fp) && total != 0) { dst = xrealloc(dst, total + 2); dst[total] = '\n'; dst[total + 1] = '\0'; return dst; } xfree(dst); return NULL; } int istgt_read_config(CONFIG *cp, const char *file) { FILE *fp; char *lp, *p; char *lp2, *q; int line; int n, n2; if (file == NULL || file[0] == '\0') return -1; fp = fopen(file, "r"); if (fp == NULL) { fprintf(stderr, "open error: %s\n", file); return -1; } cp->file = xstrdup(file); line = 1; while ((lp = fgets_line(fp)) != NULL) { /* skip spaces */ for (p = lp; *p != '\0' && isspace((int) *p); p++) ; /* skip comment, empty line */ if (p[0] == '#' || p[0] == '\0') goto next_line; /* concatenate line end with '\' */ n = strlen(p); while (n > 2 && p[n - 1] == '\n' && p[n - 2] == '\\') { n -= 2; lp2 = fgets_line(fp); if (lp2 == NULL) break; line++; n2 = strlen(lp2); q = xmalloc(n + n2 + 1); memcpy(q, p, n); memcpy(q + n, lp2, n2); q[n + n2] = '\0'; xfree(lp2); xfree(lp); p = lp = q; n += n2; } /* parse one line */ if (parse_line(cp, p) < 0) { fprintf(stderr, "parse error at line %d of %s\n", line, cp->file); } next_line: line++; xfree(lp); } fclose(fp); return 0; } int istgt_print_config(CONFIG *cp) { CF_SECTION *sp; CF_ITEM *ip; CF_VALUE *vp; if (cp == NULL) return -1; /* empty config? */ sp = cp->section; if (sp == NULL) return 0; while (sp != NULL) { printf("Section: %s\n", sp->name); ip = sp->item; while (ip != NULL) { printf(" Item: %s ", ip->key); vp = ip->val; while (vp != NULL) { printf("Val: %s ", vp->value); vp = vp->next; } printf("\n"); ip = ip->next; } sp = sp->next; } return 0; } istgt-20111008/src/istgt_lu_disk.c000644 000000 000000 00000515044 11643630071 017012 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_UUID_H #include #endif #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_crc32c.h" #include "istgt_md5.h" #include "istgt_iscsi.h" #include "istgt_lu.h" #include "istgt_proto.h" #include "istgt_scsi.h" #include "istgt_queue.h" #ifndef O_FSYNC #define O_FSYNC O_SYNC #endif //#define ISTGT_TRACE_DISK typedef enum { ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE = 0x01, ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS = 0x03, ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY = 0x05, ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY = 0x06, ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS = 0x07, ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS = 0x08, } ISTGT_LU_PR_TYPE; #define PR_ALLOW(WE,EA,ALLRR,WERR,EARR) \ ((((WE)&1) << 4) | (((EA)&1) << 3) | (((ALLRR)&1) << 2) \ | (((WERR)&1) << 1) | (((EARR)&1) << 0)) #define PR_ALLOW_WE 0x0010 #define PR_ALLOW_EA 0x0008 #define PR_ALLOW_ALLRR 0x0004 #define PR_ALLOW_WERR 0x0002 #define PR_ALLOW_EARR 0x0001 typedef struct istgt_lu_pr_key_t { uint64_t key; /* transport IDs */ char *registered_initiator_port; char *registered_target_port; /* PERSISTENT RESERVE OUT received from */ int pg_idx; /* relative target port */ int pg_tag; /* target port group */ int ninitiator_ports; char **initiator_ports; int all_tpg; } ISTGT_LU_PR_KEY; typedef struct istgt_lu_disk_t { ISTGT_LU_Ptr lu; int num; int lun; int fd; const char *file; uint64_t fsize; uint64_t foffset; uint64_t size; uint64_t blocklen; uint64_t blockcnt; #ifdef HAVE_UUID_H uuid_t uuid; #endif /* HAVE_UUID_H */ /* cache flags */ int read_cache; int write_cache; /* parts for cache */ int wbufsize; uint8_t *wbuf; uint64_t woffset; uint64_t wnbytes; int req_write_cache; int err_write_cache; /* thin provisioning */ int thin_provisioning; /* for ats */ pthread_mutex_t ats_mutex; int queue_depth; pthread_mutex_t cmd_queue_mutex; ISTGT_QUEUE cmd_queue; pthread_mutex_t wait_lu_task_mutex; ISTGT_LU_TASK_Ptr wait_lu_task; /* PERSISTENT RESERVE */ int npr_keys; ISTGT_LU_PR_KEY pr_keys[MAX_LU_RESERVE]; uint32_t pr_generation; char *rsv_port; uint64_t rsv_key; int rsv_scope; int rsv_type; /* SCSI sense code */ volatile int sense; } ISTGT_LU_DISK; #define BUILD_SENSE(SK,ASC,ASCQ) \ do { \ *sense_len = \ istgt_lu_disk_build_sense_data(spec, sense_data, \ ISTGT_SCSI_SENSE_ ## SK, \ (ASC), (ASCQ)); \ } while (0) #define BUILD_SENSE2(SK,ASC,ASCQ) \ do { \ *sense_len = \ istgt_lu_disk_build_sense_data2(spec, sense_data, \ ISTGT_SCSI_SENSE_ ## SK, \ (ASC), (ASCQ)); \ } while (0) static void istgt_lu_disk_free_pr_key(ISTGT_LU_PR_KEY *prkey); static int istgt_lu_disk_build_sense_data(ISTGT_LU_DISK *spec, uint8_t *data, int sk, int asc, int ascq); static int istgt_lu_disk_queue_abort_ITL(ISTGT_LU_DISK *spec, const char *initiator_port); static int istgt_lu_disk_open(ISTGT_LU_DISK *spec, int flags, int mode) { int rc; rc = open(spec->file, flags, mode); if (rc < 0) { return -1; } spec->fd = rc; spec->foffset = 0; return 0; } static int istgt_lu_disk_close(ISTGT_LU_DISK *spec) { int rc; if (spec->fd == -1) return 0; rc = close(spec->fd); if (rc < 0) { return -1; } spec->fd = -1; spec->foffset = 0; return 0; } #if 0 static off_t istgt_lu_disk_lseek(ISTGT_LU_DISK *spec, off_t offset, int whence) { off_t rc; rc = lseek(spec->fd, offset, whence); if (rc < 0) { return -1; } spec->foffset = offset; return rc; } #endif static int64_t istgt_lu_disk_seek(ISTGT_LU_DISK *spec, uint64_t offset) { off_t rc; rc = lseek(spec->fd, (off_t) offset, SEEK_SET); if (rc < 0) { return -1; } spec->foffset = offset; return 0; } static int64_t istgt_lu_disk_read(ISTGT_LU_DISK *spec, void *buf, uint64_t nbytes) { int64_t rc; if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) { if (spec->foffset + nbytes <= spec->fsize) { /* inside media */ rc = (int64_t) read(spec->fd, buf, (size_t) nbytes); } else if (spec->foffset >= spec->fsize) { /* outside media */ memset(buf, 0, nbytes); rc = nbytes; if (spec->foffset + nbytes >= spec->size) { rc = spec->size - spec->foffset; } } else if (spec->foffset + nbytes > spec->fsize) { /* both */ uint64_t request = spec->fsize - spec->foffset; memset(buf, 0, nbytes); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "read %"PRIu64" bytes at %"PRIu64"/%"PRIu64"\n", request, spec->foffset, spec->fsize); rc = (int64_t) read(spec->fd, buf, (size_t) request); if (rc < 0) { return -1; } if (rc != request) { /* read size < request */ if (spec->foffset + rc >= spec->size) { rc = spec->size - spec->foffset; } spec->foffset += rc; return rc; } rc = nbytes; if (spec->foffset + nbytes >= spec->size) { rc = spec->size - spec->foffset; } } else { rc = -1; } if (rc < 0) { return -1; } spec->foffset += rc; return rc; } rc = (int64_t) read(spec->fd, buf, (size_t) nbytes); if (rc < 0) { return -1; } spec->foffset += rc; return rc; } static int64_t istgt_lu_disk_write(ISTGT_LU_DISK *spec, const void *buf, uint64_t nbytes) { int64_t rc; if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) { if (spec->foffset + nbytes <= spec->fsize) { /* inside media */ rc = (int64_t) write(spec->fd, buf, (size_t) nbytes); } else if (spec->foffset + nbytes <= ISTGT_LU_MEDIA_SIZE_MIN) { /* allways write in minimum size */ rc = (int64_t) write(spec->fd, buf, (size_t) nbytes); } else if (spec->foffset >= spec->fsize) { /* outside media */ const uint8_t *p = (const uint8_t *) buf; uint64_t n; for (n = 0; n < nbytes; n++) { if (p[n] != 0) break; } if (n == nbytes) { /* write all zero (skip) */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "write zero %"PRIu64" bytes at %"PRIu64"/%"PRIu64"\n", nbytes, spec->foffset, spec->fsize); rc = nbytes; spec->foffset += rc; return rc; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "write %"PRIu64" bytes at %"PRIu64"/%"PRIu64"\n", nbytes, spec->foffset, spec->fsize); rc = (int64_t) write(spec->fd, buf, (size_t) nbytes); } else if (spec->foffset + nbytes > spec->fsize) { /* both */ rc = (int64_t) write(spec->fd, buf, (size_t) nbytes); } else { rc = -1; } if (rc < 0) { return -1; } spec->foffset += rc; if (spec->foffset > spec->fsize) { spec->fsize = spec->foffset; } return rc; } rc = (int64_t) write(spec->fd, buf, (size_t) nbytes); if (rc < 0) { return -1; } spec->foffset += rc; if (spec->foffset > spec->fsize) { spec->fsize = spec->foffset; } return rc; } static int64_t istgt_lu_disk_sync(ISTGT_LU_DISK *spec, uint64_t offset, uint64_t nbytes) { int64_t rc; rc = (int64_t) fsync(spec->fd); if (rc < 0) { return -1; } spec->foffset = offset + nbytes; return rc; } static int istgt_lu_disk_allocate(ISTGT_LU_DISK *spec) { uint8_t *data; uint64_t fsize; uint64_t size; uint64_t blocklen; uint64_t offset; uint64_t nbytes; int64_t rc; size = spec->size; blocklen = spec->blocklen; nbytes = blocklen; data = xmalloc(nbytes); memset(data, 0, nbytes); fsize = istgt_lu_get_filesize(spec->file); if (fsize > size) { xfree(data); return 0; } spec->fsize = fsize; offset = size - nbytes; rc = istgt_lu_disk_seek(spec, offset); if (rc == -1) { ISTGT_ERRLOG("lu_disk_seek() failed\n"); xfree(data); return -1; } rc = istgt_lu_disk_read(spec, data, nbytes); /* EOF is OK */ if (rc == -1) { ISTGT_ERRLOG("lu_disk_read() failed\n"); xfree(data); return -1; } if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) { /* allocate minimum size */ if (fsize < ISTGT_LU_MEDIA_SIZE_MIN) { fsize = ISTGT_LU_MEDIA_SIZE_MIN; if (size < ISTGT_LU_MEDIA_SIZE_MIN) { fsize = size; } offset = fsize - nbytes; rc = istgt_lu_disk_seek(spec, offset); if (rc == -1) { ISTGT_ERRLOG("lu_disk_seek() failed\n"); xfree(data); return -1; } rc = istgt_lu_disk_write(spec, data, nbytes); if (rc == -1 || rc != nbytes) { ISTGT_ERRLOG("lu_disk_write() failed\n"); xfree(data); return -1; } spec->fsize = fsize; spec->foffset = fsize; } } else { /* allocate complete size */ rc = istgt_lu_disk_seek(spec, offset); if (rc == -1) { ISTGT_ERRLOG("lu_disk_seek() failed\n"); xfree(data); return -1; } rc = istgt_lu_disk_write(spec, data, nbytes); if (rc == -1 || rc != nbytes) { ISTGT_ERRLOG("lu_disk_write() failed\n"); xfree(data); return -1; } spec->foffset = size; } xfree(data); return 0; } static int istgt_lu_disk_setcache(ISTGT_LU_DISK *spec) { int flags; int rc; int fd; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_disk_setcache\n"); fd = spec->fd; if (spec->read_cache) { /* not implement */ } else { /* not implement */ } flags = fcntl(fd , F_GETFL, 0); if (flags != -1) { if (spec->write_cache) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "write cache enable\n"); rc = fcntl(fd, F_SETFL, (flags & ~O_FSYNC)); spec->write_cache = 1; } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "write cache disable\n"); rc = fcntl(fd, F_SETFL, (flags | O_FSYNC)); spec->write_cache = 0; } if (rc == -1) { #if 0 ISTGT_ERRLOG("LU%d: LUN%d: fcntl(F_SETFL) failed(errno=%d)\n", spec->num, spec->lun, errno); #endif } } else { ISTGT_ERRLOG("LU%d: LUN%d: fcntl(F_GETFL) failed(errno=%d)\n", spec->num, spec->lun, errno); } return 0; } int istgt_lu_disk_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { ISTGT_LU_DISK *spec; uint64_t gb_size; uint64_t mb_size; #ifdef HAVE_UUID_H uint32_t status; #endif /* HAVE_UUID_H */ int mb_digit; int flags; int newfile; int rc; int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_disk_init\n"); printf("LU%d HDD UNIT\n", lu->num); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); lu->lun[i].spec = NULL; continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d storage\n", lu->num, i); spec = xmalloc(sizeof *spec); memset(spec, 0, sizeof *spec); spec->lu = lu; spec->num = lu->num; spec->lun = i; spec->fd = -1; if (spec->lu->lun[i].readcache) { spec->read_cache = 1; } else { spec->read_cache = 0; } if (spec->lu->lun[i].writecache) { spec->write_cache = 1; } else { spec->write_cache = 0; } if (spec->lu->istgt->swmode >= ISTGT_SWMODE_EXPERIMENTAL) { spec->wbufsize = ISTGT_LU_MAX_WRITE_CACHE_SIZE; spec->wbuf = xmalloc(spec->wbufsize); memset(spec->wbuf, 0, spec->wbufsize); } else { spec->wbufsize = 0; spec->wbuf = NULL; } spec->woffset = 0; spec->wnbytes = 0; spec->req_write_cache = 0; spec->err_write_cache = 0; spec->thin_provisioning = 0; rc = pthread_mutex_init(&spec->ats_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num); return -1; } spec->queue_depth = lu->queue_depth; rc = pthread_mutex_init(&spec->cmd_queue_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num); return -1; } istgt_queue_init(&spec->cmd_queue); rc = pthread_mutex_init(&spec->wait_lu_task_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num); return -1; } spec->wait_lu_task = NULL; spec->npr_keys = 0; for (j = 0; j < MAX_LU_RESERVE; j++) { spec->pr_keys[j].registered_initiator_port = NULL; } spec->pr_generation = 0; spec->rsv_port = NULL; spec->rsv_key = 0; spec->rsv_scope = 0; spec->rsv_type = 0; spec->sense = 0; { int sk, asc, ascq; /* POWER ON, RESET, OR BUS DEVICE RESET OCCURRED */ sk = ISTGT_SCSI_SENSE_UNIT_ATTENTION; asc = 0x29; ascq = 0x00; spec->sense = (((sk & 0xffU) << 16) | ((asc & 0xffU) << 8) | ((ascq & 0xffU) << 0)); } #ifdef HAVE_UUID_H uuid_create(&spec->uuid, &status); if (status != uuid_s_ok) { ISTGT_ERRLOG("LU%d: LUN%d: uuid_create() failed\n", lu->num, i); (void) pthread_mutex_destroy(&spec->cmd_queue_mutex); istgt_queue_destroy(&spec->cmd_queue); xfree(spec); return -1; } #endif /* HAVE_UUID_H */ spec->file = lu->lun[i].u.storage.file; spec->size = lu->lun[i].u.storage.size; spec->blocklen = lu->blocklen; if (spec->blocklen != 512 && spec->blocklen != 1024 && spec->blocklen != 2048 && spec->blocklen != 4096 && spec->blocklen != 8192 && spec->blocklen != 16384 && spec->blocklen != 32768 && spec->blocklen != 65536 && spec->blocklen != 131072 && spec->blocklen != 262144 && spec->blocklen != 524288) { ISTGT_ERRLOG("LU%d: LUN%d: invalid blocklen %"PRIu64"\n", lu->num, i, spec->blocklen); error_return: (void) pthread_mutex_destroy(&spec->cmd_queue_mutex); istgt_queue_destroy(&spec->cmd_queue); xfree(spec); return -1; } spec->blockcnt = spec->size / spec->blocklen; if (spec->blockcnt == 0) { ISTGT_ERRLOG("LU%d: LUN%d: size zero\n", lu->num, i); goto error_return; } #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d file=%s, size=%"PRIu64"\n", lu->num, i, spec->file, spec->size); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d %"PRIu64" blocks, %" PRIu64" bytes/block\n", lu->num, i, spec->blockcnt, spec->blocklen); #endif printf("LU%d: LUN%d file=%s, size=%"PRIu64"\n", lu->num, i, spec->file, spec->size); printf("LU%d: LUN%d %"PRIu64" blocks, %"PRIu64" bytes/block\n", lu->num, i, spec->blockcnt, spec->blocklen); flags = lu->readonly ? O_RDONLY : O_RDWR; newfile = 0; rc = istgt_lu_disk_open(spec, flags, 0666); if (rc < 0) { newfile = 1; flags = lu->readonly ? O_RDONLY : (O_CREAT | O_EXCL | O_RDWR); rc = istgt_lu_disk_open(spec, flags, 0666); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: open error\n", lu->num, i); goto error_return; } } if (!lu->readonly) { rc = istgt_lu_disk_allocate(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: allocate error\n", lu->num, i); goto error_return; } } rc = istgt_lu_disk_setcache(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: setcache error\n", lu->num, i); goto error_return; } gb_size = spec->size / ISTGT_LU_1GB; mb_size = (spec->size % ISTGT_LU_1GB) / ISTGT_LU_1MB; if (gb_size > 0) { mb_digit = (int) (((mb_size * 100) / 1024) / 10); #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d LUN%d %"PRIu64".%dGB %sstorage for %s\n", lu->num, i, gb_size, mb_digit, lu->readonly ? "readonly " : "", lu->name); #endif printf("LU%d: LUN%d %"PRIu64".%dGB %sstorage for %s\n", lu->num, i, gb_size, mb_digit, lu->readonly ? "readonly " : "", lu->name); } else { #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d %"PRIu64"MB %sstorage for %s\n", lu->num, i, mb_size, lu->readonly ? "readonly " : "", lu->name); #endif printf("LU%d: LUN%d %"PRIu64"MB %sstorage for %s\n", lu->num, i, mb_size, lu->readonly ? "readonly " : "", lu->name); } if (spec->lu->lun[i].serial != NULL) { printf("LU%d: LUN%d serial %s\n", lu->num, i, spec->lu->lun[i].serial); } else { printf("LU%d: LUN%d serial %s\n", lu->num, i, spec->lu->inq_serial); } printf("LU%d: LUN%d ", lu->num, i); if (spec->read_cache) { printf("read cache enabled"); } else { printf("read cache disabled"); } printf(", "); if (spec->write_cache) { printf("write cache enabled"); } else { printf("write cache disabled"); } printf("\n"); if (spec->queue_depth != 0) { printf("LU%d: LUN%d command queuing enabled, depth %d\n", lu->num, i, spec->queue_depth); } else { printf("LU%d: LUN%d command queuing disabled\n", lu->num, i); } #if 0 if (spec->write_cache && spec->wbufsize) { mb_size = (spec->wbufsize / ISTGT_LU_1MB); printf("LU%d: LUN%d write buffer %"PRIu64"MB\n", lu->num, i, mb_size); } #endif lu->lun[i].spec = spec; } return 0; } int istgt_lu_disk_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { ISTGT_LU_DISK *spec; ISTGT_LU_PR_KEY *prkey; int rc; int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_disk_shutdown\n"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } spec = (ISTGT_LU_DISK *) lu->lun[i].spec; if (!spec->lu->readonly) { rc = istgt_lu_disk_sync(spec, 0, spec->size); if (rc < 0) { //ISTGT_ERRLOG("LU%d: lu_disk_sync() failed\n", lu->num); /* ignore error */ } } rc = istgt_lu_disk_close(spec); if (rc < 0) { //ISTGT_ERRLOG("LU%d: lu_disk_close() failed\n", lu->num); /* ignore error */ } for (j = 0; j < spec->npr_keys; j++) { prkey = &spec->pr_keys[j]; istgt_lu_disk_free_pr_key(prkey); } if (spec->rsv_key != 0) { xfree(spec->rsv_port); spec->rsv_port = NULL; } rc = pthread_mutex_destroy(&spec->ats_mutex); if (rc != 0) { //ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num); /* ignore error */ } istgt_queue_destroy(&spec->cmd_queue); rc = pthread_mutex_destroy(&spec->cmd_queue_mutex); if (rc != 0) { //ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num); /* ignore error */ } rc = pthread_mutex_destroy(&spec->wait_lu_task_mutex); if (rc != 0) { //ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num); /* ignore error */ } xfree(spec->wbuf); xfree(spec); lu->lun[i].spec = NULL; } return 0; } void istgt_scsi_dump_cdb(uint8_t *cdb) { int group; int cdblen = 0; int i; if (cdb == NULL) return; group = (cdb[0] >> 5) & 0x07; switch (group) { case 0x00: /* 6byte commands */ cdblen = 6; break; case 0x01: /* 10byte commands */ cdblen = 10; break; case 0x02: /* 10byte commands */ cdblen = 10; break; case 0x03: /* reserved */ if (cdb[0] == 0x7f) { /* variable length */ cdblen = 8 + (cdb[7] & 0xff); } else { /* XXX */ cdblen = 6; } break; case 0x04: /* 16byte commands */ cdblen = 16; break; case 0x05: /* 12byte commands */ cdblen = 12; break; case 0x06: case 0x07: /* vendor specific */ cdblen = 6; break; } printf("CDB="); for (i = 0; i < cdblen; i++) { printf("%2.2x ", cdb[i]); } printf("\n"); } void istgt_strcpy_pad(uint8_t *dst, size_t size, const char *src, int pad) { size_t len; len = strlen(src); if (len < size) { memcpy(dst, src, len); memset(dst + len, pad, (size - len)); } else { memcpy(dst, src, size); } } #ifdef HAVE_UUID_H uint64_t istgt_uuid2uint64(uuid_t *uuid) { uint64_t low, mid, hi; uint64_t r; low = (uint64_t) uuid->time_low; mid = (uint64_t) uuid->time_mid; hi = (uint64_t) uuid->time_hi_and_version; r = (hi & 0xffffULL) << 48; r |= (mid & 0xffffULL) << 32; r |= (low & 0xffffffffULL); return r; } #endif /* HAVE_UUID_H */ uint64_t istgt_get_lui(const char *name, int lun) { char buf[MAX_TMPBUF]; uint32_t crc32c; uint64_t r; if (lun >= 0) { snprintf(buf, sizeof buf, "%s,%d", name, lun); } else { snprintf(buf, sizeof buf, "%s", name); } crc32c = istgt_crc32c((uint8_t *) buf, strlen(buf)); r = (uint64_t) crc32c; return r; } uint64_t istgt_get_rkey(const char *initiator_name, uint64_t lui) { ISTGT_MD5CTX md5ctx; uint8_t rkeymd5[ISTGT_MD5DIGEST_LEN]; char buf[MAX_TMPBUF]; uint64_t rkey; int idx; int i; snprintf(buf, sizeof buf, "%s,%16.16" PRIx64, initiator_name, lui); istgt_md5init(&md5ctx); istgt_md5update(&md5ctx, buf, strlen(buf)); istgt_md5final(rkeymd5, &md5ctx); rkey = 0U; idx = ISTGT_MD5DIGEST_LEN - 8; if (idx < 0) { ISTGT_WARNLOG("missing MD5 length\n"); idx = 0; } for (i = idx; i < ISTGT_MD5DIGEST_LEN; i++) { rkey |= (uint64_t) rkeymd5[i]; rkey = rkey << 8; } return rkey; } /* XXX */ #define COMPANY_ID 0xACDE48U // 24bits int istgt_lu_set_lid(uint8_t *buf, uint64_t vid) { uint64_t naa; uint64_t enc; int total; naa = 0x3; // Locally Assigned /* NAA + LOCALLY ADMINISTERED VALUE */ enc = (naa & 0xfULL) << (64-4); // 4bits enc |= vid & 0xfffffffffffffffULL; //60bits DSET64(&buf[0], enc); total = 8; return total; } int istgt_lu_set_id(uint8_t *buf, uint64_t vid) { uint64_t naa; uint64_t cid; uint64_t enc; int total; naa = 0x5; // IEEE Registered cid = COMPANY_ID; //IEEE COMPANY_ID /* NAA + COMPANY_ID + VENDOR SPECIFIC IDENTIFIER */ enc = (naa & 0xfULL) << (64-4); // 4bits enc |= (cid & 0xffffffULL) << (64-4-24); // 24bits enc |= vid & 0xfffffffffULL; //36bits DSET64(&buf[0], enc); total = 8; return total; } int istgt_lu_set_extid(uint8_t *buf, uint64_t vid, uint64_t vide) { uint64_t naa; uint64_t cid; uint64_t enc; int total; naa = 0x6; // IEEE Registered Extended cid = COMPANY_ID; //IEEE COMPANY_ID /* NAA + COMPANY_ID + VENDOR SPECIFIC IDENTIFIER */ enc = (naa & 0xfULL) << (64-4); // 4bits enc |= (cid & 0xffffffULL) << (64-4-24); // 24bits enc |= vid & 0xfffffffffULL; //36bits DSET64(&buf[0], enc); /* VENDOR SPECIFIC IDENTIFIER EXTENSION */ DSET64(&buf[8], vide); total = 16; return total; } static int istgt_lu_disk_scsi_report_luns(ISTGT_LU_Ptr lu, CONN_Ptr conn, uint8_t *cdb, int sel, uint8_t *data, int alloc_len) { uint64_t fmt_lun, lun, method; int hlen = 0, len = 0; int i; if (alloc_len < 8) { return -1; } if (sel == 0x00) { /* logical unit with addressing method */ } else if (sel == 0x01) { /* well known logical unit */ } else if (sel == 0x02) { /* logical unit */ } else { return -1; } /* LUN LIST LENGTH */ DSET32(&data[0], 0); /* Reserved */ DSET32(&data[4], 0); hlen = 8; for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); #endif continue; } if (alloc_len - (hlen + len) < 8) { return -1; } lun = (uint64_t) i; if (lu->maxlun <= 0x0100) { /* below 256 */ method = 0x00U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (lun & 0x00ffU) << 48; } else if (lu->maxlun <= 0x4000U) { /* below 16384 */ method = 0x01U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (lun & 0x3fffU) << 48; } else { /* XXX */ fmt_lun = 0; } /* LUN */ DSET64(&data[hlen + len], fmt_lun); len += 8; } /* LUN LIST LENGTH */ DSET32(&data[0], len); return hlen + len; } static int istgt_lu_disk_scsi_inquiry(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int alloc_len) { uint64_t LUI; uint8_t *cp, *cp2; uint32_t blocks; int hlen = 0, len = 0, plen, plen2; int pc; int pq, pd; int rmb; int evpd; int pg_tag; int i, j; if (alloc_len < 0xff) { return -1; } pq = 0x00; pd = SPC_PERIPHERAL_DEVICE_TYPE_DISK; rmb = 0; #if 0 LUI = istgt_uuid2uint64(&spec->uuid); #else LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU); #endif pc = cdb[2]; evpd = BGET8(&cdb[1], 0); if (evpd) { /* Vital product data */ switch (pc) { case SPC_VPD_SUPPORTED_VPD_PAGES: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; data[4] = SPC_VPD_SUPPORTED_VPD_PAGES; /* 0x00 */ data[5] = SPC_VPD_UNIT_SERIAL_NUMBER; /* 0x80 */ data[6] = SPC_VPD_DEVICE_IDENTIFICATION; /* 0x83 */ data[7] = SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES; /* 0x85 */ data[8] = SPC_VPD_EXTENDED_INQUIRY_DATA; /* 0x86 */ data[9] = SPC_VPD_MODE_PAGE_POLICY; /* 0x87 */ data[10]= SPC_VPD_SCSI_PORTS; /* 0x88 */ data[11]= 0xb0; /* SBC Block Limits */ data[12]= 0xb1; /* SBC Block Device Characteristics */ len = 13 - hlen; if (spec->thin_provisioning) { data[13]= 0xb2; /* SBC Thin Provisioning */ len++; } /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_UNIT_SERIAL_NUMBER: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; /* PRODUCT SERIAL NUMBER */ if (spec->lu->lun[spec->lun].serial != NULL) { len = strlen(spec->lu->lun[spec->lun].serial); if (len > MAX_LU_SERIAL_STRING) { len = MAX_LU_SERIAL_STRING; } istgt_strcpy_pad(&data[4], len, spec->lu->lun[spec->lun].serial, ' '); } else { len = strlen(spec->lu->inq_serial); if (len > MAX_LU_SERIAL_STRING) { len = MAX_LU_SERIAL_STRING; } istgt_strcpy_pad(&data[4], len, spec->lu->inq_serial, ' '); } /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_DEVICE_IDENTIFICATION: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Identification descriptor 1 */ /* Logical Unit */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_NAA, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ #if 0 /* 16bytes ID */ plen = istgt_lu_set_extid(&cp[4], 0, LUI); #else plen = istgt_lu_set_lid(&cp[4], LUI); #endif cp[3] = plen; len += 4 + plen; /* Identification descriptor 2 */ /* T10 VENDOR IDENTIFICATION */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* T10 VENDOR IDENTIFICATION */ istgt_strcpy_pad(&cp[4], 8, spec->lu->inq_vendor, ' '); plen = 8; /* VENDOR SPECIFIC IDENTIFIER */ /* PRODUCT IDENTIFICATION */ istgt_strcpy_pad(&cp[12], 16, spec->lu->inq_product, ' '); /* PRODUCT SERIAL NUMBER */ if (spec->lu->lun[spec->lun].serial != NULL) { istgt_strcpy_pad(&cp[28], MAX_LU_SERIAL_STRING, spec->lu->lun[spec->lun].serial, ' '); } else { istgt_strcpy_pad(&cp[28], MAX_LU_SERIAL_STRING, spec->lu->inq_serial, ' '); } plen += 16 + MAX_LU_SERIAL_STRING; cp[3] = plen; len += 4 + plen; /* Identification descriptor 3 */ /* Target Device */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_DEVICE, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp[4], MAX_TARGET_NAME, "%s", spec->lu->name); cp[3] = plen; len += 4 + plen; /* Identification descriptor 4 */ /* Target Port */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp[4], MAX_TARGET_NAME, "%s"",t,0x""%4.4x", spec->lu->name, conn->portal.tag); cp[3] = plen; len += 4 + plen; /* Identification descriptor 5 */ /* Relative Target Port */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Obsolete */ DSET16(&cp[4], 0); /* Relative Target Port Identifier */ //DSET16(&cp[6], 1); /* port1 as port A */ //DSET16(&cp[6], 2); /* port2 as port B */ DSET16(&cp[6], (uint16_t) (1 + conn->portal.idx)); plen = 4; cp[3] = plen; len += 4 + plen; /* Identification descriptor 6 */ /* Target port group */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Reserved */ DSET16(&cp[4], 0); /* TARGET PORT GROUP */ DSET16(&cp[6], (uint16_t) (conn->portal.tag)); plen = 4; cp[3] = plen; len += 4 + plen; /* Identification descriptor 7 */ /* Logical unit group */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Reserved */ DSET16(&cp[4], 0); /* LOGICAL UNIT GROUP */ DSET16(&cp[6], (uint16_t) (spec->lu->num)); plen = 4; cp[3] = plen; len += 4 + plen; /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_EXTENDED_INQUIRY_DATA: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; /* RTO(3) GRD_CHK(2) APP_CHK(1) REF_CHK(0) */ data[4] = 0; /* GROUP_SUP(4) PRIOR_SUP(3) HEADSUP(2) ORDSUP(1) SIMPSUP(0) */ data[5] = 0; if (spec->queue_depth != 0) { BDADD8(&data[5], 1, 2); /* HEADSUP */ //BDADD8(&data[5], 1, 1); /* ORDSUP */ BDADD8(&data[5], 1, 0); /* SIMPSUP */ } /* NV_SUP(1) V_SUP(0) */ data[6] = 0; /* Reserved[7-63] */ memset(&data[7], 0, (64 - 7)); len = 64 - hlen; /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; #if 0 /* Network services descriptor N */ cp = &data[hlen + len]; /* ASSOCIATION(6-5) SERVICE TYPE(4-0) */ BDSET8W(&cp[0], 0x00, 6, 2); BDADD8W(&cp[0], 0x00, 4, 5); /* Reserved */ cp[1] = 0; /* NETWORK ADDRESS LENGTH */ DSET16(&cp[2], 0); /* NETWORK ADDRESS */ cp[4] = 0; /* ... */ plen = 0; DSET16(&cp[2], plen); len += 4 + plen; #endif /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_MODE_PAGE_POLICY: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Mode page policy descriptor 1 */ cp = &data[hlen + len]; /* POLICY PAGE CODE(5-0) */ BDSET8W(&cp[0], 0x3f, 5, 6); /* all page code */ /* POLICY SUBPAGE CODE */ cp[1] = 0xff; /* all sub page */ /* MLUS(7) MODE PAGE POLICY(1-0) */ //BDSET8(&cp[2], 1, 7); /* multiple logical units share */ BDSET8(&cp[2], 0, 7); /* own copy */ BDADD8W(&cp[2], 0x00, 1, 2); /* Shared */ //BDADD8W(&cp[2], 0x01, 1, 2); /* Per target port */ //BDADD8W(&cp[2], 0x02, 1, 2); /* Per initiator port */ //BDADD8W(&cp[2], 0x03, 1, 2); /* Per I_T nexus */ /* Reserved */ cp[3] = 0; len += 4; /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_SCSI_PORTS: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Identification descriptor list */ for (i = 0; i < spec->lu->maxmap; i++) { pg_tag = spec->lu->map[i].pg_tag; /* skip same pg_tag */ for (j = 0; j < i; j++) { if (spec->lu->map[j].pg_tag == pg_tag) { goto skip_pg_tag; } } /* Identification descriptor N */ cp = &data[hlen + len]; /* Reserved */ DSET16(&cp[0], 0); /* RELATIVE PORT IDENTIFIER */ DSET16(&cp[2], (uint16_t) (1 + pg_tag)); /* Reserved */ DSET16(&cp[4], 0); /* INITIATOR PORT TRANSPORTID LENGTH */ DSET16(&cp[6], 0); /* Reserved */ DSET16(&cp[8], 0); /* TARGET PORT DESCRIPTORS LENGTH */ DSET16(&cp[10], 0); len += 12; plen2 = 0; /* Target port descriptor 1 */ cp2 = &data[hlen + len + plen2]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp2[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp2[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp2[1], 1, 7, 1); /* PIV */ BDADD8W(&cp2[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp2[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp2[2] = 0; /* IDENTIFIER LENGTH */ cp2[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp2[4], MAX_TARGET_NAME, "%s"",t,0x""%4.4x", spec->lu->name, pg_tag); cp2[3] = plen; plen2 += 4 + plen; /* TARGET PORT DESCRIPTORS LENGTH */ DSET16(&cp[10], plen2); len += plen2; skip_pg_tag: ; } /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case 0xb0: /* SBC Block Limits */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* WSNZ(0) */ BDSET8(&data[4], 0, 0); /* support zero length in WRITE SAME */ /* MAXIMUM COMPARE AND WRITE LENGTH */ blocks = ISTGT_LU_WORK_BLOCK_SIZE / (uint32_t) spec->blocklen; if (blocks > 0xff) { blocks = 0xff; } data[5] = (uint8_t) blocks; if (spec->lu->istgt->swmode == ISTGT_SWMODE_TRADITIONAL) { /* no support compare and write */ data[5] = 0; } /* force align to 4KB */ if (spec->blocklen < 4096) { blocks = 4096 / (uint32_t) spec->blocklen; /* OPTIMAL TRANSFER LENGTH GRANULARITY */ DSET16(&data[6], blocks); /* MAXIMUM TRANSFER LENGTH */ DSET32(&data[8], 0); /* no limit */ /* OPTIMAL TRANSFER LENGTH */ blocks = ISTGT_LU_WORK_BLOCK_SIZE / (uint32_t) spec->blocklen; DSET32(&data[12], blocks); /* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */ DSET32(&data[16], 0); } else { blocks = 1; /* OPTIMAL TRANSFER LENGTH GRANULARITY */ DSET16(&data[6], blocks); /* MAXIMUM TRANSFER LENGTH */ DSET32(&data[8], 0); /* no limit */ /* OPTIMAL TRANSFER LENGTH */ blocks = ISTGT_LU_WORK_BLOCK_SIZE / (uint32_t) spec->blocklen; DSET32(&data[12], blocks); /* MAXIMUM PREFETCH XDREAD XDWRITE TRANSFER LENGTH */ DSET32(&data[16], 0); } len = 20 - hlen; if (1 || spec->thin_provisioning) { /* MAXIMUM UNMAP LBA COUNT */ DSET32(&data[20], 0); /* not implement UNMAP */ /* MAXIMUM UNMAP BLOCK DESCRIPTOR COUNT */ DSET32(&data[24], 0); /* not implement UNMAP */ /* OPTIMAL UNMAP GRANULARITY */ DSET32(&data[28], 0); /* not specified */ /* UNMAP GRANULARITY ALIGNMENT */ DSET32(&data[32], (0 & 0x7fffffffU)); /* UGAVALID(7) */ BDADD8(&data[32], 0, 7); /* not valid ALIGNMENT */ /* MAXIMUM WRITE SAME LENGTH */ DSET64(&data[36], 0); /* no limit */ /* Reserved */ memset(&data[44], 0x00, 64-44); len = 64 - hlen; } DSET16(&data[2], len); break; case 0xb1: /* SBC Block Device Characteristics */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* MEDIUM ROTATION RATE */ //DSET16(&data[4], 0x0000); /* not reported */ //DSET16(&data[4], 0x0001); /* Non-rotating medium (solid state) */ //DSET16(&data[4], 5400); /* rotation rate (5400rpm) */ //DSET16(&data[4], 7200); /* rotation rate (7200rpm) */ //DSET16(&data[4], 10000); /* rotation rate (10000rpm) */ //DSET16(&data[4], 15000); /* rotation rate (15000rpm) */ DSET16(&data[4], spec->lu->lun[spec->lun].rotationrate); /* Reserved */ data[6] = 0; /* NOMINAL FORM FACTOR(3-0) */ //BDSET8W(&data[7], 0x00, 3, 4); /* not reported */ //BDSET8W(&data[7], 0x01, 3, 4); /* 5.25 inch */ //BDSET8W(&data[7], 0x02, 3, 4); /* 3.5 inch */ //BDSET8W(&data[7], 0x03, 3, 4); /* 2.5 inch */ //BDSET8W(&data[7], 0x04, 3, 4); /* 1.8 inch */ //BDSET8W(&data[7], 0x05, 3, 4); /* less 1.8 inch */ BDSET8W(&data[7], spec->lu->lun[spec->lun].formfactor, 3, 4); /* Reserved */ memset(&data[8], 0x00, 64-8); len = 64 - hlen; DSET16(&data[2], len); break; case 0xb2: /* SBC Thin Provisioning */ if (!spec->thin_provisioning) { ISTGT_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc); return -1; } /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* THRESHOLD EXPONENT */ data[4] = 0; /* DP(0) */ BDSET8(&data[5], 0, 0); /* Reserved */ DSET16(&data[6], 0); len = 6 - hlen; #if 0 /* XXX not yet */ /* PROVISIONING GROUP DESCRIPTOR ... */ DSET16(&data[8], 0); len = 8 - hlen; #endif DSET16(&data[2], len); break; default: if (pc >= 0xc0 && pc <= 0xff) { ISTGT_WARNLOG("Vendor specific INQUIRY VPD page 0x%x\n", pc); } else { ISTGT_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc); } return -1; } } else { /* Standard INQUIRY data */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* RMB(7) */ BDSET8W(&data[1], rmb, 7, 1); /* VERSION */ /* See SPC3/SBC2/MMC4/SAM2 for more details */ data[2] = SPC_VERSION_SPC3; /* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */ BDSET8W(&data[3], 2, 3, 4); /* format 2 */ BDADD8(&data[1], 1, 4); /* hierarchical support */ /* ADDITIONAL LENGTH */ data[4] = 0; hlen = 5; /* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */ data[5] = 0; //BDADD8W(&data[5], 1, 7, 1); /* storage array controller */ BDADD8W(&data[5], 0x00, 5, 2); /* Not support TPGS */ //BDADD8W(&data[5], 0x01, 5, 2); /* Only implicit */ //BDADD8W(&data[5], 0x02, 5, 2); /* Only explicit */ //BDADD8W(&data[5], 0x03, 5, 2); /* Both explicit and implicit */ /* BQUE(7) ENCSERV(6) VS(5) MULTIP(4) MCHNGR(3) ADDR16(0) */ data[6] = 0; BDADD8W(&data[6], 1, 4, 1); /* MULTIP */ /* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */ data[7] = 0; if (spec->queue_depth != 0) { BDADD8(&data[7], 1, 1); /* CMDQUE */ } /* T10 VENDOR IDENTIFICATION */ istgt_strcpy_pad(&data[8], 8, spec->lu->inq_vendor, ' '); /* PRODUCT IDENTIFICATION */ istgt_strcpy_pad(&data[16], 16, spec->lu->inq_product, ' '); /* PRODUCT REVISION LEVEL */ istgt_strcpy_pad(&data[32], 4, spec->lu->inq_revision, ' '); /* Vendor specific */ memset(&data[36], 0x20, 20); /* CLOCKING(3-2) QAS(1) IUS(0) */ data[56] = 0; /* Reserved */ data[57] = 0; /* VERSION DESCRIPTOR 1-8 */ DSET16(&data[58], 0x0960); /* iSCSI (no version claimed) */ DSET16(&data[60], 0x0300); /* SPC-3 (no version claimed) */ DSET16(&data[62], 0x0320); /* SBC-2 (no version claimed) */ DSET16(&data[64], 0x0040); /* SAM-2 (no version claimed) */ DSET16(&data[66], 0x0000); DSET16(&data[68], 0x0000); DSET16(&data[70], 0x0000); DSET16(&data[72], 0x0000); /* Reserved[74-95] */ memset(&data[74], 0, (96 - 74)); /* Vendor specific parameters[96-n] */ //data[96] = 0; len = 96 - hlen; /* ADDITIONAL LENGTH */ data[4] = len; } return hlen + len; } #define MODE_SENSE_PAGE_INIT(B,L,P,SP) \ do { \ memset((B), 0, (L)); \ if ((SP) != 0x00) { \ (B)[0] = (P) | 0x40; /* PAGE + SPF=1 */ \ (B)[1] = (SP); \ DSET16(&(B)[2], (L) - 4); \ } else { \ (B)[0] = (P); \ (B)[1] = (L) - 2; \ } \ } while (0) static int istgt_lu_disk_scsi_mode_sense_page(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int len = 0; int plen; int rc; int i; #if 0 printf("pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage); #endif #if 0 ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE: pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage); #endif if (pc == 0x00) { /* Current values */ } else if (pc == 0x01) { /* Changeable values */ if (page != 0x08) { /* not supported */ return 0; } } else if (pc == 0x02) { /* Default values */ } else { /* Saved values */ } cp = &data[len]; switch (page) { case 0x00: /* Vendor specific */ break; case 0x01: /* Read-Write Error Recovery */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Read-Write Error Recovery\n"); if (subpage != 0x00) break; plen = 0x0a + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x02: /* Disconnect-Reconnect */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Disconnect-Reconnect\n"); if (subpage != 0x00) break; plen = 0x0e + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x03: /* Obsolete (Format Device) */ break; case 0x04: /* Obsolete (Rigid Disk Geometry) */ break; case 0x05: /* Obsolete (Rigid Disk Geometry) */ break; case 0x06: /* Reserved */ break; case 0x07: /* Verify Error Recovery */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Verify Error Recovery\n"); if (subpage != 0x00) break; plen = 0x0a + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x08: /* Caching */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Caching\n"); if (subpage != 0x00) break; plen = 0x12 + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); BDADD8(&cp[0], 1, 7); /* PS */ BDADD8(&cp[2], 1, 2); /* WCE */ //BDADD8(&cp[2], 1, 0); /* RCD */ { int fd; fd = spec->fd; rc = fcntl(fd , F_GETFL, 0); if (rc != -1 && !(rc & O_FSYNC)) { BDADD8(&cp[2], 1, 2); /* WCE=1 */ } else { BDADD8(&cp[2], 0, 2); /* WCE=0 */ } } if (spec->read_cache == 0) { BDADD8(&cp[2], 1, 0); /* RCD=1 */ } else { BDADD8(&cp[2], 0, 0); /* RCD=0 */ } len += plen; break; case 0x09: /* Obsolete */ break; case 0x0a: switch (subpage) { case 0x00: /* Control */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Control\n"); plen = 0x0a + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x01: /* Control Extension */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Control Extension\n"); plen = 0x1c + 4; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0xff: /* All subpages */ len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, 0x00, &data[len], alloc_len); len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, 0x01, &data[len], alloc_len); break; default: /* 0x02-0x3e: Reserved */ break; } break; case 0x0b: /* Obsolete (Medium Types Supported) */ break; case 0x0c: /* Obsolete (Notch And Partitio) */ break; case 0x0d: /* Obsolete */ break; case 0x0e: case 0x0f: /* Reserved */ break; case 0x10: /* XOR Control */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE XOR Control\n"); if (subpage != 0x00) break; plen = 0x16 + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x11: case 0x12: case 0x13: /* Reserved */ break; case 0x14: /* Enclosure Services Management */ break; case 0x15: case 0x16: case 0x17: /* Reserved */ break; case 0x18: /* Protocol-Specific LUN */ #if 0 ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Protocol-Specific LUN\n"); if (subpage != 0x00) break; plen = 0x04 + 0x00 + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; #endif break; case 0x19: /* Protocol-Specific Port */ #if 0 ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Protocol-Specific Port\n"); if (subpage != 0x00) break; plen = 0x04 + 0x00 + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; #endif break; case 0x1a: /* Power Condition */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Power Condition\n"); if (subpage != 0x00) break; plen = 0x0a + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x1b: /* Reserved */ break; case 0x1c: /* Informational Exceptions Control */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Informational Exceptions Control\n"); if (subpage != 0x00) break; plen = 0x0a + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x1d: case 0x1e: case 0x1f: /* Reserved */ break; case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: /* Vendor-specific */ break; case 0x3f: switch (subpage) { case 0x00: /* All mode pages */ for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len); } break; case 0xff: /* All mode pages and subpages */ for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len); } for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0xff, &cp[len], alloc_len); } break; default: /* 0x01-0x3e: Reserved */ break; } } return len; } static int istgt_lu_disk_scsi_mode_sense6(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int hlen = 0, len = 0, plen; int total; int llbaa = 0; data[0] = 0; /* Mode Data Length */ data[1] = 0; /* Medium Type */ data[2] = 0; /* Device-Specific Parameter */ if (spec->lu->readonly) { BDADD8(&data[2], 1, 7); /* WP */ } data[3] = 0; /* Block Descripter Length */ hlen = 4; cp = &data[4]; if (dbd) { /* Disable Block Descripters */ len = 0; } else { if (llbaa) { /* Number of Blocks */ DSET64(&cp[0], spec->blockcnt); /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], (uint32_t) spec->blocklen); len = 16; } else { /* Number of Blocks */ if (spec->blockcnt > 0xffffffffULL) { DSET32(&cp[0], 0xffffffffUL); } else { DSET32(&cp[0], (uint32_t) spec->blockcnt); } /* Block Length */ DSET32(&cp[4], (uint32_t) spec->blocklen); len = 8; } cp += len; } data[3] = len; /* Block Descripter Length */ plen = istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len); cp += plen; total = hlen + len + plen; data[0] = total - 1; /* Mode Data Length */ return total; } static int istgt_lu_disk_scsi_mode_sense10(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int llbaa, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int hlen = 0, len = 0, plen; int total; DSET16(&data[0], 0); /* Mode Data Length */ data[2] = 0; /* Medium Type */ data[3] = 0; /* Device-Specific Parameter */ if (spec->lu->readonly) { BDADD8(&data[3], 1, 7); /* WP */ } if (llbaa) { BDSET8(&data[4], 1, 1); /* Long LBA */ } else { BDSET8(&data[4], 0, 1); /* Short LBA */ } data[5] = 0; /* Reserved */ DSET16(&data[6], 0); /* Block Descripter Length */ hlen = 8; cp = &data[8]; if (dbd) { /* Disable Block Descripters */ len = 0; } else { if (llbaa) { /* Number of Blocks */ DSET64(&cp[0], spec->blockcnt); /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], (uint32_t) spec->blocklen); len = 16; } else { /* Number of Blocks */ if (spec->blockcnt > 0xffffffffULL) { DSET32(&cp[0], 0xffffffffUL); } else { DSET32(&cp[0], (uint32_t) spec->blockcnt); } /* Block Length */ DSET32(&cp[4], (uint32_t) spec->blocklen); len = 8; } cp += len; } DSET16(&data[6], len); /* Block Descripter Length */ plen = istgt_lu_disk_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len); cp += plen; total = hlen + len + plen; DSET16(&data[0], total - 2); /* Mode Data Length */ return total; } static int istgt_lu_disk_transfer_data(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *buf, size_t bufsize, size_t len) { int rc; if (lu_cmd->lu->queue_depth == 0) { if (len > bufsize) { ISTGT_ERRLOG("bufsize(%d) too small\n", bufsize); return -1; } rc = istgt_iscsi_transfer_out(conn, lu_cmd, buf, bufsize, len); if (rc < 0) { ISTGT_ERRLOG("iscsi_transfer_out()\n"); return -1; } } return 0; } static int istgt_lu_disk_scsi_mode_select_page(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int pf, int sp, uint8_t *data, size_t len) { int ps, spf, page, subpage; int hlen, plen; int rc; if (pf == 0) { /* vendor specific */ return 0; } if (len < 1) return 0; ps = BGET8(&data[0], 7); spf = BGET8(&data[0], 6); page = data[0] & 0x3f; if (spf) { /* Sub_page mode page format */ hlen = 4; if (len < hlen) return 0; subpage = data[1]; plen = DGET16(&data[2]); } else { /* Page_0 mode page format */ hlen = 2; if (len < hlen) return 0; subpage = 0; plen = data[1]; } plen += hlen; if (len < plen) return 0; #if 0 printf("ps=%d, page=%2.2x, subpage=%2.2x\n", ps, page, subpage); #endif switch (page) { case 0x08: /* Caching */ { int wce, rcd; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Caching\n"); if (subpage != 0x00) break; if (plen != 0x12 + hlen) { /* unknown format */ break; } wce = BGET8(&data[2], 2); /* WCE */ rcd = BGET8(&data[2], 0); /* RCD */ { int fd; fd = spec->fd; rc = fcntl(fd , F_GETFL, 0); if (rc != -1) { if (wce) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Writeback cache enable\n"); rc = fcntl(fd, F_SETFL, (rc & ~O_FSYNC)); spec->write_cache = 1; } else { rc = fcntl(fd, F_SETFL, (rc | O_FSYNC)); spec->write_cache = 0; } if (rc == -1) { /* XXX */ //ISTGT_ERRLOG("fcntl(F_SETFL) failed\n"); } } } if (rcd) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Read cache disable\n"); spec->read_cache = 0; } else { spec->read_cache = 1; } } break; default: /* not supported */ break; } len -= plen; if (len != 0) { rc = istgt_lu_disk_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[plen], len); if (rc < 0) { return rc; } } return 0; } #if 0 static int istgt_lu_disk_scsi_request_sense(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, int desc, uint8_t *data, int alloc_len) { int len = 0, plen; if (alloc_len < 18) { ISTGT_ERRLOG("alloc_len(%d) too small\n", alloc_len); return -1; } /* XXX TODO: fix */ if (desc == 0) { /* fixed format */ /* NO ADDITIONAL SENSE INFORMATION */ /* BUILD_SENSE(NO_SENSE, 0x00, 0x00); */ /* VALID(7) RESPONSE CODE(6-0) */ BDSET8(&data[0], 0, 7); BDADD8W(&data[0], 0x70, 6, 7); /* Obsolete */ data[1] = 0; /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ BDSET8W(&data[2], ISTGT_SCSI_SENSE_NO_SENSE, 3, 4); /* INFORMATION */ memset(&data[3], 0, 4); /* ADDITIONAL SENSE LENGTH */ data[7] = 0; len = 8; /* COMMAND-SPECIFIC INFORMATION */ memset(&data[8], 0, 4); /* ADDITIONAL SENSE CODE */ data[12] = 0x00; /* ADDITIONAL SENSE CODE QUALIFIER */ data[13] = 0x00; /* FIELD REPLACEABLE UNIT CODE */ data[14] = 0; /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */ data[15] = 0; data[16] = 0; data[17] = 0; plen = 18 - len; /* ADDITIONAL SENSE LENGTH */ data[7] = plen; } else { /* descriptor format */ /* NO ADDITIONAL SENSE INFORMATION */ /* BUILD_SENSE(NO_SENSE, 0x00, 0x00); */ /* RESPONSE CODE(6-0) */ BDSET8W(&data[0], 0x72, 6, 7); /* SENSE KEY(3-0) */ BDSET8W(&data[1], ISTGT_SCSI_SENSE_NO_SENSE, 3, 4); /* ADDITIONAL SENSE CODE */ data[2] = 0x00; /* ADDITIONAL SENSE CODE QUALIFIER */ data[3] = 0x00; /* Reserved */ data[4] = 0; data[5] = 0; data[6] = 0; /* ADDITIONAL SENSE LENGTH */ data[7] = 0; len = 8; /* Sense data descriptor(s) */ plen = 8 - len; /* ADDITIONAL SENSE LENGTH */ data[7] = plen; } return len; } #endif static int istgt_lu_disk_scsi_report_target_port_groups(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int alloc_len) { ISTGT_Ptr istgt; ISTGT_LU_Ptr lu; uint8_t *cp; uint8_t *cp_count; int hlen = 0, len = 0, plen; int total; int pg_tag; int nports; int i, j; if (alloc_len < 0xfff) { return -1; } istgt = conn->istgt; lu = spec->lu; /* RETURN DATA LENGTH */ DSET32(&data[0], 0); hlen = 4; MTX_LOCK(&istgt->mutex); for (i = 0; i < lu->maxmap; i++) { pg_tag = lu->map[i].pg_tag; /* skip same pg_tag */ for (j = 0; j < i; j++) { if (lu->map[j].pg_tag == pg_tag) { goto skip_pg_tag; } } /* Target port group descriptor N */ cp = &data[hlen + len]; /* PREF(7) ASYMMETRIC ACCESS STATE(3-0) */ cp[0] = 0; BDSET8(&cp[0], 1, 7); /* PREF */ switch (lu->map[j].pg_aas & 0x0f) { case AAS_ACTIVE_OPTIMIZED: BDADD8W(&cp[0], AAS_ACTIVE_OPTIMIZED, 3, 4); break; case AAS_ACTIVE_NON_OPTIMIZED: BDADD8W(&cp[0], AAS_ACTIVE_NON_OPTIMIZED, 3, 4); break; case AAS_STANDBY: BDADD8W(&cp[0], AAS_STANDBY, 3, 4); break; case AAS_UNAVAILABLE: BDADD8W(&cp[0], AAS_UNAVAILABLE, 3, 4); break; case AAS_TRANSITIONING: BDADD8W(&cp[0], AAS_TRANSITIONING, 3, 4); break; default: ISTGT_ERRLOG("unsupported AAS\n"); break; } /* T_SUP(7) U_SUP(3) S_SUP(2) S_SUP AN_SUP(1) AO_SUP(0) */ cp[1] = 0; //BDADD8(&cp[1], 1, 7); /* transitioning supported */ //BDADD8(&cp[1], 1, 3); /* unavailable supported */ //BDADD8(&cp[1], 1, 2); /* standby supported */ BDADD8(&cp[1], 1, 1); /* active/non-optimized supported */ BDADD8(&cp[1], 1, 0); /* active/optimized supported */ /* TARGET PORT GROUP */ DSET16(&cp[2], pg_tag); /* Reserved */ cp[4] = 0; /* STATUS CODE */ if (lu->map[j].pg_aas & AAS_STATUS_IMPLICIT) { cp[5] = 0x02; /* by implicit */ } else if (lu->map[j].pg_aas & AAS_STATUS_STPG) { cp[5] = 0x01; /* by SET TARGET PORT GROUPS */ } else { cp[5] = 0; /* No status */ } /* Vendor specific */ cp[6] = 0; /* TARGET PORT COUNT */ cp[7] = 0; cp_count = &cp[7]; plen = 8; len += plen; nports = 0; for (j = 0; j < istgt->nportal; j ++) { if (istgt->portal[j].tag != pg_tag) continue; /* Target port descriptor(s) */ cp = &data[hlen + len]; /* Obsolete */ DSET16(&cp[0], 0); /* RELATIVE TARGET PORT IDENTIFIER */ DSET16(&cp[2], (uint16_t) (1 + istgt->portal[j].idx)); plen = 4; len += plen; nports++; } if (nports > 0xff) { ISTGT_ERRLOG("too many portals in portal group\n"); MTX_UNLOCK(&istgt->mutex); return -1; } /* TARGET PORT COUNT */ cp_count[0] = nports; skip_pg_tag: ; } MTX_UNLOCK(&istgt->mutex); total = hlen + len; if (total > alloc_len) { ISTGT_ERRLOG("alloc_len(%d) too small\n", alloc_len); return -1; } /* RETURN DATA LENGTH */ DSET32(&data[0], total - 4); return total; } static int istgt_lu_disk_scsi_set_target_port_groups(ISTGT_LU_DISK *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int len) { ISTGT_LU_Ptr lu; int pg_tag; int aas; int pg; int rc; int i; if (len < 4) { return -1; } lu = spec->lu; aas = BGET8W(&data[0], 3, 4); pg = DGET16(&data[2]); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AAS=0x%x, PG=0x%4.4x\n", aas, pg); for (i = 0; i < lu->maxmap; i++) { pg_tag = lu->map[i].pg_tag; if (pg != pg_tag) continue; switch (aas) { case AAS_ACTIVE_OPTIMIZED: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Active/optimized\n"); break; case AAS_ACTIVE_NON_OPTIMIZED: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Active/non-optimized\n"); break; #if 0 case AAS_STANDBY: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Standby\n"); break; case AAS_UNAVAILABLE: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Unavailable\n"); break; #endif case AAS_TRANSITIONING: return -1; default: ISTGT_ERRLOG("unsupported AAS 0x%x\n", aas); return -1; } lu->map[i].pg_aas = aas; lu->map[i].pg_aas |= AAS_STATUS_STPG; } len -=4; if (len != 0) { rc = istgt_lu_disk_scsi_set_target_port_groups(spec, conn, cdb, data, len); if (rc < 0) { return rc; } } return 0; } static void istgt_lu_disk_free_pr_key(ISTGT_LU_PR_KEY *prkey) { int i; if (prkey == NULL) return; xfree(prkey->registered_initiator_port); prkey->registered_initiator_port = NULL; xfree(prkey->registered_target_port); prkey->registered_target_port = NULL; prkey->pg_idx = 0; prkey->pg_tag = 0; for (i = 0; i < prkey->ninitiator_ports; i++) { xfree(prkey->initiator_ports[i]); prkey->initiator_ports[i] = NULL; } xfree(prkey->initiator_ports); prkey->initiator_ports = NULL; prkey->all_tpg = 0; } static ISTGT_LU_PR_KEY * istgt_lu_disk_find_pr_key(ISTGT_LU_DISK *spec, const char *initiator_port, const char *target_port, uint64_t key) { ISTGT_LU_PR_KEY *prkey; int i; /* return pointer if I_T nexus is registered */ #ifdef ISTGT_TRACE_DISK ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "find prkey=0x%16.16"PRIx64", port=%s\n", key, ((initiator_port != NULL) ? initiator_port : "N/A")); #endif /* ISTGT_TRACE_DISK */ if (initiator_port == NULL) return NULL; for (i = 0; i < spec->npr_keys; i++) { prkey = &spec->pr_keys[i]; if (prkey == NULL) continue; #ifdef ISTGT_TRACE_DISK if (key != 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "prkey=0x%16.16"PRIx64"\n", prkey->key); } #endif /* ISTGT_TRACE_DISK */ if (key != 0 && prkey->key != key) continue; #ifdef ISTGT_TRACE_DISK ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "pript=%s, ipt=%s\n", prkey->registered_initiator_port, initiator_port); #endif /* ISTGT_TRACE_DISK */ if (strcmp(prkey->registered_initiator_port, initiator_port) == 0) { #ifdef ISTGT_TRACE_DISK ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "prtpt=%s, tpt=%s\n", prkey->registered_target_port, target_port); #endif /* ISTGT_TRACE_DISK */ if (prkey->all_tpg != 0 || target_port == NULL || strcmp(prkey->registered_target_port, target_port) == 0) { return prkey; } } } return NULL; } static int istgt_lu_disk_remove_other_pr_key(ISTGT_LU_DISK *spec, CONN_Ptr conn, const char *initiator_port, const char *target_port, uint64_t key) { ISTGT_LU_PR_KEY *prkey, *prkey1, *prkey2; int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "remove other prkey=0x%16.16"PRIx64", port=%s\n", key, ((initiator_port != NULL) ? initiator_port : "N/A")); for (i = 0; i < spec->npr_keys; i++) { prkey = &spec->pr_keys[i]; if (prkey == NULL) continue; if (key == 0 || prkey->key == key) continue; if (initiator_port == NULL || strcasecmp(prkey->registered_initiator_port, initiator_port) == 0) continue; if (prkey->all_tpg != 0 || target_port == NULL || strcasecmp(prkey->registered_target_port, target_port) == 0) continue; istgt_lu_disk_free_pr_key(prkey); for (j = i; j < spec->npr_keys - 1; j++) { prkey1 = &spec->pr_keys[j]; prkey2 = &spec->pr_keys[j+1]; prkey1->registered_initiator_port = prkey2->registered_initiator_port; prkey2->registered_initiator_port = NULL; prkey1->registered_target_port = prkey2->registered_target_port; prkey2->registered_target_port = NULL; prkey1->pg_idx = prkey2->pg_idx; prkey2->pg_idx = 0; prkey1->pg_tag = prkey2->pg_tag; prkey2->pg_tag = 0; prkey1->ninitiator_ports = prkey2->ninitiator_ports; prkey2->ninitiator_ports = 0; prkey1->initiator_ports = prkey2->initiator_ports; prkey2->initiator_ports = NULL; prkey1->all_tpg = prkey2->all_tpg; prkey2->all_tpg = 0; } spec->npr_keys--; } return 0; } static int istgt_lu_disk_remove_pr_key(ISTGT_LU_DISK *spec, CONN_Ptr conn, const char *initiator_port, const char *target_port, uint64_t key) { ISTGT_LU_PR_KEY *prkey, *prkey1, *prkey2; int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "remove prkey=0x%16.16"PRIx64", port=%s\n", key, ((initiator_port != NULL) ? initiator_port : "N/A")); for (i = 0; i < spec->npr_keys; i++) { prkey = &spec->pr_keys[i]; if (prkey == NULL) continue; if (key != 0 && prkey->key != key) continue; if (initiator_port != NULL && strcasecmp(prkey->registered_initiator_port, initiator_port) != 0) continue; if (prkey->all_tpg == 0 && target_port != NULL && strcasecmp(prkey->registered_target_port, target_port) != 0) continue; istgt_lu_disk_free_pr_key(prkey); for (j = i; j < spec->npr_keys - 1; j++) { prkey1 = &spec->pr_keys[j]; prkey2 = &spec->pr_keys[j+1]; prkey1->registered_initiator_port = prkey2->registered_initiator_port; prkey2->registered_initiator_port = NULL; prkey1->registered_target_port = prkey2->registered_target_port; prkey2->registered_target_port = NULL; prkey1->pg_idx = prkey2->pg_idx; prkey2->pg_idx = 0; prkey1->pg_tag = prkey2->pg_tag; prkey2->pg_tag = 0; prkey1->ninitiator_ports = prkey2->ninitiator_ports; prkey2->ninitiator_ports = 0; prkey1->initiator_ports = prkey2->initiator_ports; prkey2->initiator_ports = NULL; prkey1->all_tpg = prkey2->all_tpg; prkey2->all_tpg = 0; } spec->npr_keys--; } return 0; } static int istgt_lu_parse_transport_id(char **tid, uint8_t *data, int len) { int fc, pi; int hlen, plen; if (tid == NULL) return -1; if (data == NULL) return -1; fc = BGET8W(&data[0], 7, 2); pi = BGET8W(&data[0], 3, 4); if (fc != 0) { ISTGT_ERRLOG("FORMAT CODE != 0\n"); return -1; } if (pi != SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME) { ISTGT_ERRLOG("PROTOCOL IDENTIFIER != ISCSI\n"); return -1; } /* PROTOCOL IDENTIFIER = 0x05 */ hlen = 4; /* ADDITIONAL LENGTH */ plen = DGET16(&data[2]); if (plen > len) { ISTGT_ERRLOG("invalid length %d (expected %d)\n", plen, len); return -1; } if (plen > MAX_ISCSI_NAME) { ISTGT_ERRLOG("invalid length %d (expected %d)\n", plen, MAX_ISCSI_NAME); return -1; } /* ISCSI NAME */ *tid = xmalloc(plen + 1); memcpy(*tid, data, plen); (*tid)[plen] = '\0'; strlwr(*tid); return hlen + plen; } static int istgt_lu_disk_scsi_persistent_reserve_in(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, int sa, uint8_t *data, int alloc_len) { ISTGT_LU_PR_KEY *prkey; uint8_t *sense_data; int *sense_len; uint8_t *cp; int hlen = 0, len = 0, plen; int total; int i; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; cp = &data[hlen + len]; total = 0; switch (sa) { case 0x00: /* READ KEYS */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ KEYS\n"); /* PRGENERATION */ DSET32(&data[0], spec->pr_generation); /* ADDITIONAL LENGTH */ DSET32(&data[4], 0); hlen = 8; for (i = 0; i < spec->npr_keys; i++) { prkey = &spec->pr_keys[i]; /* reservation key N */ cp = &data[hlen + len]; DSET64(&cp[0], prkey->key); len += 8; } total = hlen + len; /* ADDITIONAL LENGTH */ DSET32(&data[4], total - hlen); break; case 0x01: /* READ RESERVATION */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ RESERVATION\n"); /* PRGENERATION */ DSET32(&data[0], spec->pr_generation); /* ADDITIONAL LENGTH */ DSET32(&data[4], 0); hlen = 8; if (spec->rsv_key != 0) { /* RESERVATION KEY */ DSET64(&data[8], spec->rsv_key); /* Obsolete */ DSET32(&data[16], 0); /* Reserved */ data[20] = 0; /* SCOPE(7-4) TYPE(3-0) */ BDSET8W(&data[21], spec->rsv_scope, 7, 4); BDADD8W(&data[21], spec->rsv_type, 3, 4); /* Obsolete */ DSET16(&data[22], 0); len = 24 - hlen; } total = hlen + len; /* ADDITIONAL LENGTH */ DSET32(&data[4], total - hlen); break; case 0x02: /* REPORT CAPABILITIES */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT CAPABILITIES\n"); /* LENGTH */ DSET16(&data[0], 0x0008); /* CRH(4) SIP_C(3) ATP_C(2) PTPL_C(0) */ data[2] = 0; //BDADD8(&data[2], 1, 4); /* Compatible Reservation Handling */ BDADD8(&data[2], 1, 3); /* Specify Initiator Ports Capable */ BDADD8(&data[2], 1, 2); /* All Target Ports Capable */ //BDADD8(&data[2], 1, 0); /* Persist Through Power Loss Capable */ /* TMV(7) PTPL_A(0) */ data[3] = 0; //BDADD8(&data[2], 1, 7); /* Type Mask Valid */ //BDADD8(&data[2], 1, 0); /* Persist Through Power Loss Activated */ /* PERSISTENT RESERVATION TYPE MASK */ DSET16(&data[4], 0); /* Reserved */ DSET16(&data[6], 0); hlen = 8; total = hlen + len; break; case 0x03: /* READ FULL STATUS */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ FULL STATUS\n"); /* PRGENERATION */ DSET32(&data[0], spec->pr_generation); /* ADDITIONAL LENGTH */ DSET32(&data[4], 0); hlen = 8; for (i = 0; i < spec->npr_keys; i++) { prkey = &spec->pr_keys[i]; /* Full status descriptors N */ cp = &data[hlen + len]; /* RESERVATION KEY */ DSET64(&cp[0], prkey->key); /* Reserved */ DSET64(&cp[8], 0); /* ALL_TG_PT(1) R_HOLDER(0) */ cp[12] = 0; if (prkey->all_tpg) { BDADD8(&cp[12], 1, 1); } /* SCOPE(7-4) TYPE(3-0) */ cp[13] = 0; if (spec->rsv_key != 0) { if (spec->rsv_key == prkey->key) { BDADD8(&cp[12], 1, 0); BDADD8W(&cp[13], spec->rsv_scope & 0x0f, 7, 4); BDADD8W(&cp[13], spec->rsv_type & 0x0f, 3, 4); } } /* Reserved */ DSET32(&cp[14], 0); /* RELATIVE TARGET PORT IDENTIFIER */ DSET16(&cp[18], 1 + prkey->pg_idx); /* ADDITIONAL DESCRIPTOR LENGTH */ DSET32(&cp[20], 0); /* TRANSPORTID */ plen = snprintf((char *) &cp[24], MAX_INITIATOR_NAME, "%s", prkey->registered_initiator_port); /* ADDITIONAL DESCRIPTOR LENGTH */ DSET32(&cp[20], plen); len += 24 + plen; } total = hlen + len; /* ADDITIONAL LENGTH */ DSET32(&data[4], total - hlen); break; default: ISTGT_ERRLOG("unsupported service action 0x%x\n", sa); /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return total; } static int istgt_lu_disk_scsi_persistent_reserve_out(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, int sa, int scope, int type, uint8_t *data, int len) { ISTGT_LU_PR_KEY *prkey; uint8_t *sense_data; int *sense_len; char *old_rsv_port = NULL; char **initiator_ports; int maxports, nports; int plen, total; uint64_t rkey; uint64_t sarkey; int spec_i_pt, all_tg_pt, aptpl; int task_abort; int idx; int rc; int i; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; rkey = DGET64(&data[0]); sarkey = DGET64(&data[8]); spec_i_pt = BGET8(&data[20], 3); all_tg_pt = BGET8(&data[20], 2); aptpl = BGET8(&data[20], 0); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sa=0x%2.2x, key=0x%16.16"PRIx64", sakey=0x%16.16"PRIx64 ", ipt=%d, tgpt=%d, aptpl=%d\n", sa, rkey, sarkey, spec_i_pt, all_tg_pt, aptpl); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "port=%s\n", conn->initiator_port); switch (sa) { case 0x00: /* REGISTER */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REGISTER\n"); if (aptpl != 0) { /* Activate Persist Through Power Loss */ ISTGT_ERRLOG("unsupport Activate Persist Through Power Loss\n"); /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* lost reservations if daemon restart */ prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, 0); if (prkey == NULL) { /* unregistered port */ if (rkey != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } if (sarkey != 0) { /* XXX check spec_i_pt */ } } else { /* registered port */ if (spec_i_pt) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, rkey); if (prkey == NULL) { /* not found key */ lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } /* remove existing keys */ rc = istgt_lu_disk_remove_pr_key(spec, conn, conn->initiator_port, conn->target_port, rkey); if (rc < 0) { ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } /* unregister? */ if (sarkey == 0) { lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } goto do_register; case 0x01: /* RESERVE */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE\n"); prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, 0); if (prkey == NULL) { /* unregistered port */ lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, rkey); if (prkey == NULL) { /* not found key */ lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } if (spec->rsv_key == 0) { /* no reservation */ } else { if (prkey->key != spec->rsv_key) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } if (strcasecmp(spec->rsv_port, conn->initiator_port) != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } if (g_trace_flag) { ISTGT_WARNLOG("LU%d: duplicate reserve\n", spec->lu->num); } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } if (scope != 0x00) { // !LU_SCOPE /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) { ISTGT_ERRLOG("unsupported type 0x%x\n", type); /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* establish reservation by key */ xfree(spec->rsv_port); spec->rsv_port = xstrdup(conn->initiator_port); strlwr(spec->rsv_port); spec->rsv_key = rkey; spec->rsv_scope = scope; spec->rsv_type = type; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: reserved (scope=%d, type=%d) by key=0x%16.16" PRIx64"\n", spec->lu->num, scope, type, rkey); break; case 0x02: /* RELEASE */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE\n"); prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, 0); if (prkey == NULL) { /* unregistered port */ lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, rkey); if (prkey == NULL) { /* not found key */ lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } if (spec->rsv_key == 0) { /* no reservation */ lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } if (prkey->key != spec->rsv_key) { /* INVALID RELEASE OF PERSISTENT RESERVATION */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x04); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (strcasecmp(spec->rsv_port, conn->initiator_port) != 0) { /* INVALID RELEASE OF PERSISTENT RESERVATION */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x04); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (scope != 0x00) { // !LU_SCOPE /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) { ISTGT_ERRLOG("unsupported type 0x%x\n", type); /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (spec->rsv_scope != scope || spec->rsv_type != type) { /* INVALID RELEASE OF PERSISTENT RESERVATION */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x04); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* release reservation by key */ xfree(spec->rsv_port); spec->rsv_port = NULL; spec->rsv_key = 0; spec->rsv_scope = 0; spec->rsv_type = 0; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: released (scope=%d, type=%d) by key=0x%16.16" PRIx64"\n", spec->lu->num, scope, type, rkey); break; case 0x03: /* CLEAR */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "CLEAR\n"); prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, 0); if (prkey == NULL) { /* unregistered port */ lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } /* release reservation */ xfree(spec->rsv_port); spec->rsv_port = NULL; spec->rsv_key = 0; spec->rsv_scope = 0; spec->rsv_type = 0; /* remove all registrations */ for (i = 0; i < spec->npr_keys; i++) { prkey = &spec->pr_keys[i]; istgt_lu_disk_free_pr_key(prkey); } spec->npr_keys = 0; break; case 0x04: /* PREEMPT */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PREEMPT\n"); task_abort = 0; do_preempt: prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, 0); if (prkey == NULL) { /* unregistered port */ lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; return -1; } if (spec->rsv_key == 0) { /* no reservation */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "no reservation\n"); /* remove registration */ rc = istgt_lu_disk_remove_pr_key(spec, conn, NULL, NULL, sarkey); if (rc < 0) { ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* update generation */ spec->pr_generation++; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "rsv_key=0x%16.16"PRIx64"\n", spec->rsv_key); if (spec->rsv_type == ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS || spec->rsv_type == ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) { if (sarkey != 0) { /* remove registration */ rc = istgt_lu_disk_remove_pr_key(spec, conn, NULL, NULL, sarkey); if (rc < 0) { ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* update generation */ spec->pr_generation++; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } else { /* remove other registrations */ rc = istgt_lu_disk_remove_other_pr_key(spec, conn, conn->initiator_port, conn->target_port, rkey); if (rc < 0) { ISTGT_ERRLOG("lu_disk_remove_other_pr_key() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (scope != 0x00) { // !LU_SCOPE /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) { ISTGT_ERRLOG("unsupported type 0x%x\n", type); /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* release reservation */ //xfree(spec->rsv_port); old_rsv_port = spec->rsv_port; spec->rsv_port = NULL; spec->rsv_key = 0; spec->rsv_scope = 0; spec->rsv_type = 0; /* establish new reservation */ spec->rsv_port = xstrdup(conn->initiator_port); strlwr(spec->rsv_port); spec->rsv_key = rkey; spec->rsv_scope = scope; spec->rsv_type = type; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: reserved (scope=%d, type=%d)" "by key=0x%16.16"PRIx64"\n", spec->lu->num, scope, type, rkey); /* update generation */ spec->pr_generation++; /* XXX TODO fix */ if (task_abort) { /* abort all tasks for preempted I_T nexus */ if (old_rsv_port != NULL) { rc = istgt_lu_disk_queue_abort_ITL(spec, old_rsv_port); xfree(old_rsv_port); old_rsv_port = NULL; if (rc < 0) { /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } } if (old_rsv_port != NULL) { xfree(old_rsv_port); old_rsv_port = NULL; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } } prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, rkey); if (prkey == NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "prkey == NULL\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "prkey key=%16.16"PRIx64"\n", prkey->key); } if (prkey == NULL || sarkey != spec->rsv_key) { if (sarkey != 0) { /* remove registration */ rc = istgt_lu_disk_remove_pr_key(spec, conn, NULL, NULL, sarkey); if (rc < 0) { ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } else { /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } /* remove registration */ rc = istgt_lu_disk_remove_pr_key(spec, conn, NULL, NULL, sarkey); if (rc < 0) { ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (scope != 0x00) { // !LU_SCOPE /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY && type != ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS && type != ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS) { ISTGT_ERRLOG("unsupported type 0x%x\n", type); /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* release reservation */ //xfree(spec->rsv_port); old_rsv_port = spec->rsv_port; spec->rsv_port = NULL; spec->rsv_key = 0; spec->rsv_scope = 0; spec->rsv_type = 0; /* establish new reservation */ spec->rsv_port = xstrdup(conn->initiator_port); strlwr(spec->rsv_port); spec->rsv_key = rkey; spec->rsv_scope = scope; spec->rsv_type = type; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: reserved (scope=%d, type=%d) by key=0x%16.16" PRIx64"\n", spec->lu->num, scope, type, rkey); /* update generation */ spec->pr_generation++; /* XXX TODO fix */ if (task_abort) { /* abort all tasks for preempted I_T nexus */ if (old_rsv_port != NULL) { rc = istgt_lu_disk_queue_abort_ITL(spec, old_rsv_port); xfree(old_rsv_port); old_rsv_port = NULL; if (rc < 0) { /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } } if (old_rsv_port != NULL) { xfree(old_rsv_port); old_rsv_port = NULL; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case 0x05: /* PREEMPT AND ABORT */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PREEMPT AND ABORT\n"); task_abort = 1; goto do_preempt; case 0x06: /* REGISTER AND IGNORE EXISTING KEY */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REGISTER AND IGNORE EXISTING KEY\n"); if (aptpl != 0) { /* Activate Persist Through Power Loss */ ISTGT_ERRLOG("unsupport Activate Persist Through Power Loss\n"); /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* lost reservations if daemon restart */ prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, 0); if (prkey == NULL) { /* unregistered port */ if (sarkey != 0) { if (spec_i_pt) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } } else { /* registered port */ if (spec_i_pt) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } /* remove existing keys */ rc = istgt_lu_disk_remove_pr_key(spec, conn, conn->initiator_port, conn->target_port, 0); if (rc < 0) { ISTGT_ERRLOG("lu_disk_remove_pr_key() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* unregister? */ if (sarkey == 0) { lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } do_register: /* specified port? */ nports = 0; initiator_ports = NULL; if (spec_i_pt) { if (len < 28) { /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* TRANSPORTID PARAMETER DATA LENGTH */ plen = DGET32(&data[24]); if (28 + plen > len) { ISTGT_ERRLOG("invalid length %d (expect %d)\n", len, 28 + plen); /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "TransportID parameter data length %d\n", plen); if (plen != 0) { maxports = MAX_LU_RESERVE_IPT; initiator_ports = xmalloc(sizeof (char *) * maxports); memset(initiator_ports, 0, sizeof (char *) * maxports); nports = 0; total = 0; while (total < plen) { if (nports >= MAX_LU_RESERVE_IPT) { ISTGT_ERRLOG("maximum transport IDs\n"); /* INSUFFICIENT REGISTRATION RESOURCES */ BUILD_SENSE(ILLEGAL_REQUEST, 0x55, 0x04); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } rc = istgt_lu_parse_transport_id (&initiator_ports[nports], &data[24] + total, plen - total); if (rc < 0) { /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "got TransportID %s\n", initiator_ports[nports]); total += rc; nports++; } } /* check all port unregistered? */ for (i = 0; i < nports; i++) { prkey = istgt_lu_disk_find_pr_key(spec, initiator_ports[i], NULL, 0); if (prkey != NULL) { /* registered port */ /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } /* OK, all port unregistered */ idx = spec->npr_keys; if (idx + nports >= MAX_LU_RESERVE) { /* INSUFFICIENT REGISTRATION RESOURCES */ BUILD_SENSE(ILLEGAL_REQUEST, 0x55, 0x04); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* register each I_T nexus */ for (i = 0; i < nports; i++) { prkey = &spec->pr_keys[idx + i]; /* register new key */ prkey->key = sarkey; /* command received port */ prkey->registered_initiator_port = xstrdup(conn->initiator_port); strlwr(prkey->registered_initiator_port); prkey->registered_target_port = xstrdup(conn->target_port); strlwr(prkey->registered_target_port); prkey->pg_idx = conn->portal.idx; prkey->pg_tag = conn->portal.tag; /* specified ports */ prkey->ninitiator_ports = 0; prkey->initiator_ports = NULL; prkey->all_tpg = (all_tg_pt) ? 1 : 0; } spec->npr_keys = idx + nports; } idx = spec->npr_keys; if (idx >= MAX_LU_RESERVE) { /* INSUFFICIENT REGISTRATION RESOURCES */ BUILD_SENSE(ILLEGAL_REQUEST, 0x55, 0x04); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } prkey = &spec->pr_keys[idx]; /* register new key */ prkey->key = sarkey; /* command received port */ prkey->registered_initiator_port = xstrdup(conn->initiator_port); strlwr(prkey->registered_initiator_port); prkey->registered_target_port = xstrdup(conn->target_port); strlwr(prkey->registered_target_port); prkey->pg_idx = conn->portal.idx; prkey->pg_tag = conn->portal.tag; /* specified ports */ prkey->ninitiator_ports = nports; prkey->initiator_ports = initiator_ports; prkey->all_tpg = (all_tg_pt) ? 1 : 0; /* count up keys */ idx++; spec->npr_keys = idx; /* update generation */ spec->pr_generation++; break; case 0x07: /* REGISTER AND MOVE */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REGISTER AND MOVE\n"); /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; default: ISTGT_ERRLOG("unsupported service action 0x%x\n", sa); /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_disk_check_pr(ISTGT_LU_DISK *spec, CONN_Ptr conn, int pr_allow) { ISTGT_LU_PR_KEY *prkey; #ifdef ISTGT_TRACE_DISK ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "RSV_KEY=0x%16.16"PRIx64", RSV_TYPE=0x%x, PR_ALLOW=0x%x\n", spec->rsv_key, spec->rsv_type, pr_allow); #endif /* ISTGT_TRACE_DISK */ prkey = istgt_lu_disk_find_pr_key(spec, conn->initiator_port, conn->target_port, 0); if (prkey != NULL) { #ifdef ISTGT_TRACE_DISK ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "PRKEY(0x%16.16"PRIx64") found for %s\n", prkey->key, conn->initiator_port); #endif /* ISTGT_TRACE_DISK */ if (spec->rsv_key == prkey->key) { /* reservation holder */ return 0; } switch (spec->rsv_type) { case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: if (pr_allow & PR_ALLOW_ALLRR) return 0; return -1; case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: if (pr_allow & PR_ALLOW_ALLRR) return 0; return -1; case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: if (pr_allow & PR_ALLOW_ALLRR) return 0; return -1; case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: if (pr_allow & PR_ALLOW_ALLRR) return 0; return -1; } } else { #ifdef ISTGT_TRACE_DISK ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "PRKEY not found for %s\n", conn->initiator_port); #endif /* ISTGT_TRACE_DISK */ switch (spec->rsv_type) { case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_ALL_REGISTRANTS: if (pr_allow & PR_ALLOW_WERR) return 0; return -1; case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE_REGISTRANTS_ONLY: if (pr_allow & PR_ALLOW_WERR) return 0; return -1; case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_ALL_REGISTRANTS: if (pr_allow & PR_ALLOW_EARR) return 0; return -1; case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS_REGISTRANTS_ONLY: if (pr_allow & PR_ALLOW_EARR) return 0; return -1; } } #ifdef ISTGT_TRACE_DISK ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "non registrans type\n"); #endif /* ISTGT_TRACE_DISK */ /* any I_T nexus */ switch (spec->rsv_type) { case ISTGT_LU_PR_TYPE_WRITE_EXCLUSIVE: if (pr_allow & PR_ALLOW_WE) return 0; return -1; case ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS: if (pr_allow & PR_ALLOW_EA) return 0; return -1; } /* NG */ return -1; } static int istgt_lu_disk_scsi_release(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_CMD lu_cmd2; uint8_t *sense_data; int *sense_len; uint64_t LUI; uint64_t rkey; uint8_t cdb[10]; uint8_t PRO_data[24]; int parameter_len; int rc; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; memset(&lu_cmd2, 0, sizeof lu_cmd2); lu_cmd2.sense_data = lu_cmd->sense_data; lu_cmd2.sense_data_len = lu_cmd->sense_data_len; memset(&cdb, 0, sizeof cdb); parameter_len = sizeof PRO_data; LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU); rkey = istgt_get_rkey(conn->initiator_name, LUI); /* issue release action of PERSISTENT RESERVE OUT */ cdb[0] = SPC_PERSISTENT_RESERVE_OUT; BDSET8W(&cdb[1], 0x02, 4, 5); /* RELEASE */ BDSET8W(&cdb[2], 0x00, 7, 4); /* LU_SCOPE */ BDADD8W(&cdb[2], 0x03, 3, 4); /* ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS */ cdb[3] = 0; cdb[4] = 0; DSET32(&cdb[5], parameter_len); cdb[9] = 0; lu_cmd2.cdb = &cdb[0]; memset(&PRO_data, 0, sizeof PRO_data); DSET64(&PRO_data[0], rkey); // RESERVATION KEY DSET64(&PRO_data[8], 0); rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2, 0x02, 0x00, 0x03, PRO_data, parameter_len); if (rc < 0) { lu_cmd->status = lu_cmd2.status; if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) { return -1; } /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* issue unregister action of PERSISTENT RESERVE OUT */ cdb[0] = SPC_PERSISTENT_RESERVE_OUT; BDSET8W(&cdb[1], 0x06, 4, 5); /* REGISTER AND IGNORE EXISTING KEY */ cdb[2] = 0; cdb[3] = 0; cdb[4] = 0; DSET32(&cdb[5], parameter_len); cdb[9] = 0; lu_cmd2.cdb = &cdb[0]; memset(&PRO_data, 0, sizeof PRO_data); DSET64(&PRO_data[0], rkey); // RESERVATION KEY DSET64(&PRO_data[8], 0); // unregister rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2, 0x06, 0, 0, PRO_data, parameter_len); if (rc < 0) { lu_cmd->status = lu_cmd2.status; if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) { return -1; } /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_disk_scsi_reserve(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_CMD lu_cmd2; uint8_t *sense_data; int *sense_len; uint64_t LUI; uint64_t rkey; uint8_t cdb[10]; uint8_t PRO_data[24]; int parameter_len; int rc; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; memset(&lu_cmd2, 0, sizeof lu_cmd2); lu_cmd2.sense_data = lu_cmd->sense_data; lu_cmd2.sense_data_len = lu_cmd->sense_data_len; memset(&cdb, 0, sizeof cdb); parameter_len = sizeof PRO_data; LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU); rkey = istgt_get_rkey(conn->initiator_name, LUI); /* issue register action of PERSISTENT RESERVE OUT */ cdb[0] = SPC_PERSISTENT_RESERVE_OUT; BDSET8W(&cdb[1], 0x06, 4, 5); /* REGISTER AND IGNORE EXISTING KEY */ cdb[2] = 0; cdb[3] = 0; cdb[4] = 0; DSET32(&cdb[5], parameter_len); cdb[9] = 0; lu_cmd2.cdb = &cdb[0]; memset(&PRO_data, 0, sizeof PRO_data); DSET64(&PRO_data[0], 0); DSET64(&PRO_data[8], rkey); // SERVICE ACTION RESERVATION KEY rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2, 0x06, 0, 0, PRO_data, parameter_len); if (rc < 0) { lu_cmd->status = lu_cmd2.status; if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) { return -1; } /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* issue reserve action of PERSISTENT RESERVE OUT */ cdb[0] = SPC_PERSISTENT_RESERVE_OUT; BDSET8W(&cdb[1], 0x01, 4, 5); /* RESERVE */ BDSET8W(&cdb[2], 0x00, 7, 4); /* LU_SCOPE */ BDADD8W(&cdb[2], 0x03, 3, 4); /* ISTGT_LU_PR_TYPE_EXCLUSIVE_ACCESS */ cdb[3] = 0; cdb[4] = 0; DSET32(&cdb[5], parameter_len); cdb[9] = 0; lu_cmd2.cdb = &cdb[0]; memset(&PRO_data, 0, sizeof PRO_data); DSET64(&PRO_data[0], rkey); // RESERVATION KEY DSET64(&PRO_data[8], 0); rc = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, &lu_cmd2, 0x01, 0x00, 0x03, PRO_data, parameter_len); if (rc < 0) { lu_cmd->status = lu_cmd2.status; if (lu_cmd->status == ISTGT_SCSI_STATUS_RESERVATION_CONFLICT) { return -1; } /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_disk_lbread(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint8_t *data; uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; int64_t rc; if (len == 0) { lu_cmd->data = NULL; lu_cmd->data_len = 0; return 0; } maxlba = spec->blockcnt; llen = (uint64_t) len; blen = spec->blocklen; offset = lba * blen; nbytes = llen * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Read: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } if (nbytes > lu_cmd->iobufsize) { ISTGT_ERRLOG("nbytes(%u) > iobufsize(%u)\n", nbytes, lu_cmd->iobufsize); return -1; } data = lu_cmd->iobuf; rc = istgt_lu_disk_seek(spec, offset); if (rc < 0) { ISTGT_ERRLOG("lu_disk_seek() failed\n"); return -1; } rc = istgt_lu_disk_read(spec, data, nbytes); if (rc < 0) { ISTGT_ERRLOG("lu_disk_read() failed\n"); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Read %"PRId64"/%"PRIu64" bytes\n", rc, nbytes); lu_cmd->data = data; lu_cmd->data_len = rc; return 0; } static int istgt_lu_disk_lbwrite(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint8_t *data; uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; int64_t rc; if (len == 0) { lu_cmd->data_len = 0; return 0; } maxlba = spec->blockcnt; llen = (uint64_t) len; blen = spec->blocklen; offset = lba * blen; nbytes = llen * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Write: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } if (nbytes > lu_cmd->iobufsize) { ISTGT_ERRLOG("nbytes(%u) > iobufsize(%u)\n", nbytes, lu_cmd->iobufsize); return -1; } data = lu_cmd->iobuf; rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, nbytes); if (rc < 0) { ISTGT_ERRLOG("lu_disk_transfer_data() failed\n"); return -1; } if (spec->lu->readonly) { ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num); return -1; } spec->req_write_cache = 0; rc = istgt_lu_disk_seek(spec, offset); if (rc < 0) { ISTGT_ERRLOG("lu_disk_seek() failed\n"); return -1; } rc = istgt_lu_disk_write(spec, data, nbytes); if (rc < 0 || rc != nbytes) { ISTGT_ERRLOG("lu_disk_write() failed\n"); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n", rc, nbytes); lu_cmd->data_len = rc; return 0; } static int istgt_lu_disk_lbwrite_same(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint8_t *data; uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; uint64_t nblocks; uint64_t wblocks; int64_t rc; maxlba = spec->blockcnt; llen = (uint64_t) len; if (llen == 0) { if (lba >= maxlba) { ISTGT_ERRLOG("end of media\n"); return -1; } llen = maxlba - lba; } blen = spec->blocklen; offset = lba * blen; nbytes = 1 * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Write Same: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } if (nbytes > lu_cmd->iobufsize) { ISTGT_ERRLOG("nbytes(%u) > iobufsize(%u)\n", nbytes, lu_cmd->iobufsize); return -1; } data = lu_cmd->iobuf; rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, nbytes); if (rc < 0) { ISTGT_ERRLOG("lu_disk_transfer_data() failed\n"); return -1; } if (spec->lu->readonly) { ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num); return -1; } if (conn->workbuf == NULL) { conn->worksize = ISTGT_LU_WORK_BLOCK_SIZE; conn->workbuf = xmalloc(conn->worksize); } wblocks = (int64_t)conn->worksize / nbytes; if (wblocks == 0) { ISTGT_ERRLOG("work buffer is too small\n"); return -1; } spec->req_write_cache = 0; rc = istgt_lu_disk_seek(spec, offset); if (rc < 0) { ISTGT_ERRLOG("lu_disk_seek() failed\n"); return -1; } #if 0 nblocks = 0; while (nblocks < llen) { rc = istgt_lu_disk_write(spec, data, nbytes); if (rc < 0 || rc != nbytes) { ISTGT_ERRLOG("lu_disk_write() failed\n"); return -1; } nblocks++; } #else nblocks = 0; while (nblocks < wblocks) { memcpy(conn->workbuf + (nblocks * nbytes), data, nbytes); nblocks++; } nblocks = 0; while (nblocks < llen) { uint64_t reqblocks = DMIN64(wblocks, (llen - nblocks)); rc = istgt_lu_disk_write(spec, conn->workbuf, (reqblocks * nbytes)); if (rc < 0 || rc != (reqblocks * nbytes)) { ISTGT_ERRLOG("lu_disk_write() failed\n"); return -1; } nblocks += reqblocks; } #endif ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n", (nblocks * nbytes), (llen * nbytes)); lu_cmd->data_len = nbytes; return 0; } static int istgt_lu_disk_lbwrite_ats(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint8_t *tmp; uint8_t *data; uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; int64_t rc; uint8_t *sense_data; int *sense_len; if (len == 0) { lu_cmd->data_len = 0; return 0; } sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; maxlba = spec->blockcnt; llen = (uint64_t) len; blen = spec->blocklen; offset = lba * blen; nbytes = llen * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Write ATS: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } if (nbytes > lu_cmd->iobufsize) { ISTGT_ERRLOG("nbytes(%u) > iobufsize(%u)\n", nbytes, lu_cmd->iobufsize); return -1; } data = lu_cmd->iobuf; rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, nbytes * 2); if (rc < 0) { ISTGT_ERRLOG("lu_disk_transfer_data() failed\n"); return -1; } if (spec->lu->readonly) { ISTGT_ERRLOG("LU%d: readonly unit\n", spec->lu->num); return -1; } tmp = xmalloc(nbytes); spec->req_write_cache = 0; /* start atomic test and set */ MTX_LOCK(&spec->ats_mutex); rc = istgt_lu_disk_seek(spec, offset); if (rc < 0) { MTX_UNLOCK(&spec->ats_mutex); ISTGT_ERRLOG("lu_disk_seek() failed\n"); xfree(tmp); return -1; } rc = istgt_lu_disk_read(spec, tmp, nbytes); if (rc < 0 || rc != nbytes) { MTX_UNLOCK(&spec->ats_mutex); ISTGT_ERRLOG("lu_disk_read() failed\n"); xfree(tmp); return -1; } #if 0 ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "ATS VERIFY", data, nbytes); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "ATS WRITE", data + nbytes, nbytes); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "ATS DATA", tmp, nbytes); #endif if (memcmp(tmp, data, nbytes) != 0) { MTX_UNLOCK(&spec->ats_mutex); //ISTGT_ERRLOG("compare failed\n"); xfree(tmp); /* MISCOMPARE DURING VERIFY OPERATION */ BUILD_SENSE(MISCOMPARE, 0x1d, 0x00); return -1; } rc = istgt_lu_disk_seek(spec, offset); if (rc < 0) { MTX_UNLOCK(&spec->ats_mutex); ISTGT_ERRLOG("lu_disk_seek() failed\n"); xfree(tmp); return -1; } rc = istgt_lu_disk_write(spec, data + nbytes, nbytes); if (rc < 0 || rc != nbytes) { MTX_UNLOCK(&spec->ats_mutex); ISTGT_ERRLOG("lu_disk_write() failed\n"); xfree(tmp); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Wrote %"PRId64"/%"PRIu64" bytes\n", rc, nbytes); MTX_UNLOCK(&spec->ats_mutex); /* end atomic test and set */ xfree(tmp); lu_cmd->data_len = nbytes * 2; return 0; } static int istgt_lu_disk_lbsync(ISTGT_LU_DISK *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lba, uint32_t len) { uint64_t maxlba; uint64_t llen; uint64_t blen; uint64_t offset; uint64_t nbytes; int64_t rc; if (len == 0) { return 0; } maxlba = spec->blockcnt; llen = (uint64_t) len; blen = spec->blocklen; offset = lba * blen; nbytes = llen * blen; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Sync: max=%"PRIu64", lba=%"PRIu64", len=%u\n", maxlba, lba, len); if (lba >= maxlba || llen > maxlba || lba > (maxlba - llen)) { ISTGT_ERRLOG("end of media\n"); return -1; } rc = istgt_lu_disk_sync(spec, offset, nbytes); if (rc < 0) { ISTGT_ERRLOG("lu_disk_sync() failed\n"); return -1; } return 0; } int istgt_lu_scsi_build_sense_data(uint8_t *data, int sk, int asc, int ascq) { uint8_t *cp; int resp_code; int hlen = 0, len = 0, plen; int total; resp_code = 0x70; /* Current + Fixed format */ /* SenseLength */ DSET16(&data[0], 0); hlen = 2; /* Sense Data */ cp = &data[hlen + len]; /* VALID(7) RESPONSE CODE(6-0) */ BDSET8(&cp[0], 1, 7); BDADD8W(&cp[0], resp_code, 6, 7); /* Obsolete */ cp[1] = 0; /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ BDSET8W(&cp[2], sk, 3, 4); /* INFORMATION */ memset(&cp[3], 0, 4); /* ADDITIONAL SENSE LENGTH */ cp[7] = 0; len = 8; /* COMMAND-SPECIFIC INFORMATION */ memset(&cp[8], 0, 4); /* ADDITIONAL SENSE CODE */ cp[12] = asc; /* ADDITIONAL SENSE CODE QUALIFIER */ cp[13] = ascq; /* FIELD REPLACEABLE UNIT CODE */ cp[14] = 0; /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */ cp[15] = 0; cp[16] = 0; cp[17] = 0; /* Additional sense bytes */ //data[18] = 0; plen = 18 - len; /* ADDITIONAL SENSE LENGTH */ cp[7] = plen; total = hlen + len + plen; /* SenseLength */ DSET16(&data[0], total - 2); return total; } static int istgt_lu_disk_build_sense_data(ISTGT_LU_DISK *spec, uint8_t *data, int sk, int asc, int ascq) { int rc; rc = istgt_lu_scsi_build_sense_data(data, sk, asc, ascq); if (rc < 0) { return -1; } return rc; } int istgt_lu_scsi_build_sense_data2(uint8_t *data, int sk, int asc, int ascq) { uint8_t *cp; int resp_code; int hlen = 0, len = 0, plen; int total; resp_code = 0x71; /* Deferred + Fixed format */ /* SenseLength */ DSET16(&data[0], 0); hlen = 2; /* Sense Data */ cp = &data[hlen + len]; /* VALID(7) RESPONSE CODE(6-0) */ BDSET8(&cp[0], 1, 7); BDADD8W(&cp[0], resp_code, 6, 7); /* Obsolete */ cp[1] = 0; /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ BDSET8W(&cp[2], sk, 3, 4); /* INFORMATION */ memset(&cp[3], 0, 4); /* ADDITIONAL SENSE LENGTH */ cp[7] = 0; len = 8; /* COMMAND-SPECIFIC INFORMATION */ memset(&cp[8], 0, 4); /* ADDITIONAL SENSE CODE */ cp[12] = asc; /* ADDITIONAL SENSE CODE QUALIFIER */ cp[13] = ascq; /* FIELD REPLACEABLE UNIT CODE */ cp[14] = 0; /* SKSV(7) SENSE KEY SPECIFIC(6-0,7-0,7-0) */ cp[15] = 0; cp[16] = 0; cp[17] = 0; /* Additional sense bytes */ //data[18] = 0; plen = 18 - len; /* ADDITIONAL SENSE LENGTH */ cp[7] = plen; total = hlen + len + plen; /* SenseLength */ DSET16(&data[0], total - 2); return total; } static int istgt_lu_disk_build_sense_data2(ISTGT_LU_DISK *spec, uint8_t *data, int sk, int asc, int ascq) { int rc; rc = istgt_lu_scsi_build_sense_data2(data, sk, asc, ascq); if (rc < 0) { return -1; } return rc; } int istgt_lu_disk_reset(ISTGT_LU_Ptr lu, int lun) { ISTGT_LU_DISK *spec; int flags; int rc; if (lu == NULL) { return -1; } if (lun >= lu->maxlun) { return -1; } if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) { return -1; } if (lu->lun[lun].type != ISTGT_LU_LUN_TYPE_STORAGE) { return -1; } spec = (ISTGT_LU_DISK *) lu->lun[lun].spec; #if 0 if (spec->lock) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "unlock by reset\n"); spec->lock = 0; } #endif if (lu->queue_depth != 0) { rc = istgt_lu_disk_queue_clear_all(lu, lun); if (rc < 0) { ISTGT_ERRLOG("lu_disk_queue_clear_all() failed\n"); return -1; } } /* re-open file */ if (!spec->lu->readonly) { rc = istgt_lu_disk_sync(spec, 0, spec->size); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_disk_sync() failed\n", lu->num, lun); /* ignore error */ } } rc = istgt_lu_disk_close(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_disk_close() failed\n", lu->num, lun); /* ignore error */ } flags = lu->readonly ? O_RDONLY : O_RDWR; rc = istgt_lu_disk_open(spec, flags, 0666); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_disk_open() failed\n", lu->num, lun); return -1; } return 0; } static int istgt_lu_disk_queue_clear_internal(ISTGT_LU_DISK *spec, const char *initiator_port, int all_cmds, uint32_t CmdSN) { ISTGT_LU_TASK_Ptr lu_task; ISTGT_QUEUE saved_queue; time_t now; int rc; if (spec == NULL) return -1; if (all_cmds != 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by port=%s\n", initiator_port); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by port=%s, CmdSN=%u\n", initiator_port, CmdSN); } istgt_queue_init(&saved_queue); now = time(NULL); MTX_LOCK(&spec->cmd_queue_mutex); while (1) { lu_task = istgt_queue_dequeue(&spec->cmd_queue); if (lu_task == NULL) break; if (((all_cmds != 0) || (lu_task->lu_cmd.CmdSN == CmdSN)) && (strcasecmp(lu_task->initiator_port, initiator_port) == 0)) { ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%u cleared\n", lu_task->lu_cmd.CmdSN, lu_task->lu_cmd.cdb[0], (now - lu_task->create_time)); rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { MTX_UNLOCK(&spec->cmd_queue_mutex); ISTGT_ERRLOG("lu_destory_task() failed\n"); goto error_return; } continue; } rc = istgt_queue_enqueue(&saved_queue, lu_task); if (rc < 0) { MTX_UNLOCK(&spec->cmd_queue_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } } while (1) { lu_task = istgt_queue_dequeue(&saved_queue); if (lu_task == NULL) break; rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task); if (rc < 0) { MTX_UNLOCK(&spec->cmd_queue_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } } MTX_UNLOCK(&spec->cmd_queue_mutex); /* check wait task */ MTX_LOCK(&spec->wait_lu_task_mutex); lu_task = spec->wait_lu_task; if (lu_task != NULL) { if (((all_cmds != 0) || (lu_task->lu_cmd.CmdSN == CmdSN)) && (strcasecmp(lu_task->initiator_port, initiator_port) == 0)) { /* conn had gone? */ rc = pthread_mutex_trylock(&lu_task->trans_mutex); if (rc == 0) { ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%u aborted\n", lu_task->lu_cmd.CmdSN, lu_task->lu_cmd.cdb[0], (now - lu_task->create_time)); /* force error */ lu_task->error = 1; lu_task->abort = 1; rc = pthread_cond_broadcast(&lu_task->trans_cond); if (rc != 0) { /* ignore error */ } MTX_UNLOCK(&lu_task->trans_mutex); } } } MTX_UNLOCK(&spec->wait_lu_task_mutex); rc = istgt_queue_count(&saved_queue); if (rc != 0) { ISTGT_ERRLOG("temporary queue is not empty\n"); goto error_return; } istgt_queue_destroy(&saved_queue); return 0; error_return: istgt_queue_destroy(&saved_queue); return -1; } static int istgt_lu_disk_queue_abort_ITL(ISTGT_LU_DISK *spec, const char *initiator_port) { int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue abort by port=%s\n", initiator_port); rc = istgt_lu_disk_queue_clear_internal(spec, initiator_port, 1, 0U); /* ALL, CmdSN=0 */ return rc; } int istgt_lu_disk_queue_clear_IT(CONN_Ptr conn, ISTGT_LU_Ptr lu) { ISTGT_LU_DISK *spec; int rc; int i; if (lu == NULL) return -1; for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); #endif continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } spec = (ISTGT_LU_DISK *) lu->lun[i].spec; if (spec == NULL) { continue; } rc = istgt_lu_disk_queue_clear_ITL(conn, lu, i); if (rc < 0) { return -1; } } return 0; } int istgt_lu_disk_queue_clear_ITL(CONN_Ptr conn, ISTGT_LU_Ptr lu, int lun) { ISTGT_LU_DISK *spec; int rc; if (lu == NULL) return -1; if (lun >= lu->maxlun) return -1; spec = (ISTGT_LU_DISK *) lu->lun[lun].spec; if (spec == NULL) return -1; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by name=%s, port=%s\n", conn->initiator_name, conn->initiator_port); rc = istgt_lu_disk_queue_clear_internal(spec, conn->initiator_port, 1, 0U); /* ALL, CmdSN=0 */ return rc; } int istgt_lu_disk_queue_clear_ITLQ(CONN_Ptr conn, ISTGT_LU_Ptr lu, int lun, uint32_t CmdSN) { ISTGT_LU_DISK *spec; int rc; if (lu == NULL) return -1; if (lun >= lu->maxlun) return -1; spec = (ISTGT_LU_DISK *) lu->lun[lun].spec; if (spec == NULL) return -1; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "queue clear by name=%s, port=%s\n", conn->initiator_name, conn->initiator_port); rc = istgt_lu_disk_queue_clear_internal(spec, conn->initiator_port, 0, CmdSN); return rc; } int istgt_lu_disk_queue_clear_all(ISTGT_LU_Ptr lu, int lun) { ISTGT_LU_TASK_Ptr lu_task; ISTGT_LU_DISK *spec; time_t now; int rc; if (lu == NULL) return -1; if (lun >= lu->maxlun) return -1; if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) { return -1; } if (lu->lun[lun].type != ISTGT_LU_LUN_TYPE_STORAGE) { return -1; } spec = (ISTGT_LU_DISK *) lu->lun[lun].spec; if (spec == NULL) return -1; now = time(NULL); MTX_LOCK(&spec->cmd_queue_mutex); while (1) { lu_task = istgt_queue_dequeue(&spec->cmd_queue); if (lu_task == NULL) break; ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%u cleared\n", lu_task->lu_cmd.CmdSN, lu_task->lu_cmd.cdb[0], (now - lu_task->create_time)); rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { MTX_UNLOCK(&spec->cmd_queue_mutex); ISTGT_ERRLOG("lu_destory_task() failed\n"); return -1; } } MTX_UNLOCK(&spec->cmd_queue_mutex); /* check wait task */ MTX_LOCK(&spec->wait_lu_task_mutex); lu_task = spec->wait_lu_task; if (lu_task != NULL) { /* conn had gone? */ rc = pthread_mutex_trylock(&lu_task->trans_mutex); if (rc == 0) { ISTGT_LOG("CmdSN(%u), OP=0x%x, ElapsedTime=%u aborted\n", lu_task->lu_cmd.CmdSN, lu_task->lu_cmd.cdb[0], (now - lu_task->create_time)); /* force error */ lu_task->error = 1; lu_task->abort = 1; rc = pthread_cond_broadcast(&lu_task->trans_cond); if (rc != 0) { /* ignore error */ } MTX_UNLOCK(&lu_task->trans_mutex); } } MTX_UNLOCK(&spec->wait_lu_task_mutex); MTX_LOCK(&spec->cmd_queue_mutex); rc = istgt_queue_count(&spec->cmd_queue); MTX_UNLOCK(&spec->cmd_queue_mutex); if (rc != 0) { ISTGT_ERRLOG("cmd queue is not empty\n"); return -1; } return 0; } int istgt_lu_disk_queue(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_TASK_Ptr lu_task; ISTGT_LU_Ptr lu; ISTGT_LU_DISK *spec; uint8_t *data; uint8_t *cdb; uint32_t allocation_len; int data_len; int data_alloc_len; uint8_t *sense_data; int *sense_len; int lun_i; int maxq; int qcnt; int rc; if (lu_cmd == NULL) return -1; lu = lu_cmd->lu; if (lu == NULL) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } spec = NULL; cdb = lu_cmd->cdb; data = lu_cmd->data; data_alloc_len = lu_cmd->alloc_len; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; lun_i = istgt_lu_islun2lun(lu_cmd->lun); if (lun_i >= lu->maxlun) { #ifdef ISTGT_TRACE_DISK ISTGT_ERRLOG("LU%d: LUN%4.4"PRIx64" invalid\n", lu->num, lun_i); #endif /* ISTGT_TRACE_DISK */ if (cdb[0] == SPC_INQUIRY) { allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], 0x03, 7, 3); BDADD8W(&data[0], 0x1f, 4, 5); data_len = 96; memset(&data[1], 0, data_len - 1); /* ADDITIONAL LENGTH */ data[4] = data_len - 5; lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return ISTGT_LU_TASK_RESULT_IMMEDIATE; } else { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return ISTGT_LU_TASK_RESULT_IMMEDIATE; } } spec = (ISTGT_LU_DISK *) lu->lun[lun_i].spec; if (spec == NULL) { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return ISTGT_LU_TASK_RESULT_IMMEDIATE; } /* ready to enqueue, spec is valid for LUN access */ /* allocate task and copy LU_CMD(PDU) */ lu_task = xmalloc(sizeof *lu_task); memset(lu_task, 0, sizeof *lu_task); rc = istgt_lu_create_task(conn, lu_cmd, lu_task, lun_i); if (rc < 0) { ISTGT_ERRLOG("lu_create_task() failed\n"); xfree(lu_task); return -1; } MTX_LOCK(&lu->queue_mutex); /* enqueue SCSI command */ MTX_LOCK(&spec->cmd_queue_mutex); rc = istgt_queue_count(&spec->cmd_queue); maxq = spec->queue_depth * lu->istgt->MaxSessions; if (rc > maxq) { MTX_UNLOCK(&spec->cmd_queue_mutex); MTX_UNLOCK(&lu->queue_mutex); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_TASK_SET_FULL; rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); return -1; } return ISTGT_LU_TASK_RESULT_QUEUE_FULL; } qcnt = rc; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Queue(%d), CmdSN=%u, OP=0x%x, LUN=0x%16.16"PRIx64"\n", qcnt, lu_cmd->CmdSN, lu_cmd->cdb[0], lu_cmd->lun); /* enqueue task to LUN */ switch (lu_cmd->Attr_bit) { case 0x03: /* Head of Queue */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Head of Queue\n"); rc = istgt_queue_enqueue_first(&spec->cmd_queue, lu_task); break; case 0x00: /* Untagged */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Untagged\n"); rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task); break; case 0x01: /* Simple */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Simple\n"); rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task); break; case 0x02: /* Ordered */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Ordered\n"); rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task); break; case 0x04: /* ACA */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert ACA\n"); rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task); break; default: /* Reserved */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "insert Reserved Attribute\n"); rc = istgt_queue_enqueue(&spec->cmd_queue, lu_task); break; } MTX_UNLOCK(&spec->cmd_queue_mutex); if (rc < 0) { MTX_UNLOCK(&lu->queue_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); error_return: rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); return -1; } return -1; } /* notify LUN thread */ rc = pthread_cond_broadcast(&lu->queue_cond); MTX_UNLOCK(&lu->queue_mutex); if (rc != 0) { ISTGT_ERRLOG("LU%d: cond_broadcast() failed\n", lu->num); goto error_return; } return ISTGT_LU_TASK_RESULT_QUEUE_OK; } int istgt_lu_disk_queue_count(ISTGT_LU_Ptr lu, int *lun) { ISTGT_LU_DISK *spec; int qcnt; int luns; int i; if (lun == NULL) return -1; i = *lun; if (i >= lu->maxlun) { *lun = 0; i = 0; } qcnt = 0; for (luns = lu->maxlun; luns >= 0 ; luns--) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { goto next_lun; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_STORAGE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); goto next_lun; } spec = (ISTGT_LU_DISK *) lu->lun[i].spec; if (spec == NULL) { goto next_lun; } MTX_LOCK(&spec->cmd_queue_mutex); qcnt = istgt_queue_count(&spec->cmd_queue); MTX_UNLOCK(&spec->cmd_queue_mutex); if (qcnt > 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d queue(%d)\n", lu->num, i, qcnt); *lun = spec->lun; break; } next_lun: i++; if (i >= lu->maxlun) { i = 0; } } return qcnt; } int istgt_lu_disk_queue_start(ISTGT_LU_Ptr lu, int lun) { ISTGT_Ptr istgt; ISTGT_LU_DISK *spec; ISTGT_LU_TASK_Ptr lu_task; CONN_Ptr conn; ISTGT_LU_CMD_Ptr lu_cmd; struct timespec abstime; time_t now; uint8_t *iobuf; char tmp[1]; int abort_task = 0; int rc; if (lun < 0 || lun >= lu->maxlun) { return -1; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LU%d: LUN%d queue start\n", lu->num, lun); spec = (ISTGT_LU_DISK *) lu->lun[lun].spec; if (spec == NULL) return -1; MTX_LOCK(&spec->cmd_queue_mutex); lu_task = istgt_queue_dequeue(&spec->cmd_queue); MTX_UNLOCK(&spec->cmd_queue_mutex); if (lu_task == NULL) { /* cleared or empty queue */ return 0; } lu_task->thread = pthread_self(); conn = lu_task->conn; istgt = conn->istgt; lu_cmd = &lu_task->lu_cmd; /* XXX need pre-allocate? */ #if 0 /* allocated in istgt_lu_create_task() */ lu_task->data = xmalloc(lu_cmd->alloc_len); lu_task->sense_data = xmalloc(lu_cmd->sense_alloc_len); lu_task->iobuf = NULL; #endif lu_cmd->data = lu_task->data; lu_cmd->data_len = 0; lu_cmd->sense_data = lu_task->sense_data; lu_cmd->sense_data_len = 0; tmp[0] = 'Q'; if (lu_cmd->W_bit) { if (lu_cmd->pdu->data_segment_len >= lu_cmd->transfer_len) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d Task Write Immediate Start\n", lu->num, lun); #if 0 iobuf = xmalloc(lu_cmd->pdu->data_segment_len); memcpy(iobuf, lu_cmd->pdu->data, lu_cmd->pdu->data_segment_len); lu_task->iobuf = iobuf; #else iobuf = lu_cmd->pdu->data; lu_task->dup_iobuf = 1; #endif lu_cmd->iobuf = iobuf; MTX_LOCK(&lu_cmd->lu->mutex); rc = istgt_lu_disk_execute(conn, lu_cmd); MTX_UNLOCK(&lu_cmd->lu->mutex); if (rc < 0) { ISTGT_ERRLOG("lu_disk_execute() failed\n"); error_return: rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); return -1; } return -1; } lu_task->execute = 1; /* response */ if (conn->use_sender == 0) { MTX_LOCK(&conn->task_queue_mutex); rc = istgt_queue_enqueue(&conn->task_queue, lu_task); MTX_UNLOCK(&conn->task_queue_mutex); if (rc < 0) { ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } rc = write(conn->task_pipe[1], tmp, 1); if(rc < 0 || rc != 1) { ISTGT_ERRLOG("write() failed\n"); goto error_return; } } else { MTX_LOCK(&conn->result_queue_mutex); rc = istgt_queue_enqueue(&conn->result_queue, lu_task); if (rc < 0) { MTX_UNLOCK(&conn->result_queue_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } rc = pthread_cond_broadcast(&conn->result_queue_cond); MTX_UNLOCK(&conn->result_queue_mutex); if (rc != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); goto error_return; } } #if 0 /* write cache */ if (spec->req_write_cache) { MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_write_cache(spec, conn); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("disk_write_cache() failed\n"); return -1; } } #endif } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d Task Write Start\n", lu->num, lun); #if 0 MTX_LOCK(&spec->wait_lu_task_mutex); spec->wait_lu_task = NULL; MTX_UNLOCK(&spec->wait_lu_task_mutex); #endif rc = pthread_mutex_init(&lu_task->trans_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); goto error_return; } rc = pthread_cond_init(&lu_task->trans_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); goto error_return; } rc = pthread_cond_init(&lu_task->exec_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); goto error_return; } lu_task->use_cond = 1; #if 0 lu_cmd->iobufsize = lu_cmd->transfer_len + 65536; iobuf = xmalloc(lu_cmd->iobufsize); lu_task->iobuf = iobuf; #else lu_cmd->iobufsize = lu_task->lu_cmd.iobufsize; iobuf = lu_task->iobuf; #endif lu_cmd->iobuf = iobuf; lu_task->req_transfer_out = 1; memset(&abstime, 0, sizeof abstime); abstime.tv_sec = 0; abstime.tv_nsec = 0; MTX_LOCK(&lu_task->trans_mutex); MTX_LOCK(&conn->task_queue_mutex); rc = istgt_queue_enqueue(&conn->task_queue, lu_task); MTX_UNLOCK(&conn->task_queue_mutex); if (rc < 0) { MTX_UNLOCK(&lu_task->trans_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } rc = write(conn->task_pipe[1], tmp, 1); if(rc < 0 || rc != 1) { MTX_UNLOCK(&lu_task->trans_mutex); ISTGT_ERRLOG("write() failed\n"); goto error_return; } now = time(NULL); abstime.tv_sec = now + (lu_task->condwait / 1000); abstime.tv_nsec = (lu_task->condwait % 1000) * 1000000; #if 0 ISTGT_LOG("wait CmdSN=%u\n", lu_task->lu_cmd.CmdSN); #endif MTX_LOCK(&spec->wait_lu_task_mutex); spec->wait_lu_task = lu_task; MTX_UNLOCK(&spec->wait_lu_task_mutex); while (lu_task->req_transfer_out == 1) { rc = pthread_cond_timedwait(&lu_task->trans_cond, &lu_task->trans_mutex, &abstime); if (rc == ETIMEDOUT) { if (lu_task->req_transfer_out == 1) { lu_task->error = 1; MTX_LOCK(&spec->wait_lu_task_mutex); spec->wait_lu_task = NULL; MTX_UNLOCK(&spec->wait_lu_task_mutex); MTX_UNLOCK(&lu_task->trans_mutex); ISTGT_ERRLOG("timeout trans_cond CmdSN=%u\n", lu_task->lu_cmd.CmdSN); /* timeout */ return -1; } /* OK cond */ rc = 0; break; } if (lu_task->error != 0) { rc = -1; break; } if (rc != 0) { break; } } MTX_LOCK(&spec->wait_lu_task_mutex); spec->wait_lu_task = NULL; MTX_UNLOCK(&spec->wait_lu_task_mutex); MTX_UNLOCK(&lu_task->trans_mutex); if (rc != 0) { if (rc < 0) { lu_task->error = 1; if (lu_task->abort) { ISTGT_WARNLOG("transfer abort CmdSN=%u\n", lu_task->lu_cmd.CmdSN); return -2; } else { ISTGT_ERRLOG("transfer error CmdSN=%u\n", lu_task->lu_cmd.CmdSN); return -1; } } if (rc == ETIMEDOUT) { lu_task->error = 1; ISTGT_ERRLOG("timeout trans_cond CmdSN=%u\n", lu_task->lu_cmd.CmdSN); return -1; } lu_task->error = 1; ISTGT_ERRLOG("cond_timedwait rc=%d\n", rc); return -1; } if (lu_task->req_execute == 0) { ISTGT_ERRLOG("wrong request\n"); goto error_return; } MTX_LOCK(&lu_cmd->lu->mutex); rc = istgt_lu_disk_execute(conn, lu_cmd); MTX_UNLOCK(&lu_cmd->lu->mutex); if (rc < 0) { lu_task->error = 1; ISTGT_ERRLOG("lu_disk_execute() failed\n"); goto error_return; } lu_task->execute = 1; /* response */ if (conn->use_sender == 0) { MTX_LOCK(&conn->task_queue_mutex); rc = istgt_queue_enqueue(&conn->task_queue, lu_task); MTX_UNLOCK(&conn->task_queue_mutex); if (rc < 0) { ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } rc = write(conn->task_pipe[1], tmp, 1); if(rc < 0 || rc != 1) { ISTGT_ERRLOG("write() failed\n"); goto error_return; } } else { MTX_LOCK(&conn->result_queue_mutex); rc = istgt_queue_enqueue(&conn->result_queue, lu_task); if (rc < 0) { MTX_UNLOCK(&conn->result_queue_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } rc = pthread_cond_broadcast(&conn->result_queue_cond); MTX_UNLOCK(&conn->result_queue_mutex); if (rc != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); goto error_return; } } #if 0 /* write cache */ if (spec->req_write_cache) { MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_write_cache(spec, conn); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("disk_write_cache() failed\n"); return -1; } } #endif } } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d Task Read Start\n", lu->num, lun); #if 0 lu_cmd->iobufsize = lu_cmd->transfer_len + 65536; iobuf = xmalloc(lu_cmd->iobufsize); lu_task->iobuf = iobuf; #else lu_cmd->iobufsize = lu_task->lu_cmd.iobufsize; iobuf = lu_task->iobuf; #endif lu_cmd->iobuf = iobuf; MTX_LOCK(&lu_cmd->lu->mutex); rc = istgt_lu_disk_execute(conn, lu_cmd); MTX_UNLOCK(&lu_cmd->lu->mutex); if (rc < 0) { ISTGT_ERRLOG("lu_disk_execute() failed\n"); goto error_return; } lu_task->execute = 1; /* response */ if (conn->use_sender == 0) { MTX_LOCK(&conn->task_queue_mutex); rc = istgt_queue_enqueue(&conn->task_queue, lu_task); MTX_UNLOCK(&conn->task_queue_mutex); if (rc < 0) { ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } rc = write(conn->task_pipe[1], tmp, 1); if(rc < 0 || rc != 1) { ISTGT_ERRLOG("write() failed\n"); goto error_return; } } else { MTX_LOCK(&conn->result_queue_mutex); rc = istgt_queue_enqueue(&conn->result_queue, lu_task); if (rc < 0) { MTX_UNLOCK(&conn->result_queue_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); goto error_return; } rc = pthread_cond_broadcast(&conn->result_queue_cond); MTX_UNLOCK(&conn->result_queue_mutex); if (rc != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); goto error_return; } } } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LU%d: LUN%d queue end\n", lu->num, lun); if (abort_task) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Abort Task\n"); return -1; } return 0; } int istgt_lu_disk_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_Ptr lu; ISTGT_LU_DISK *spec; uint8_t *data; uint8_t *cdb; uint32_t allocation_len; int data_len; int data_alloc_len; uint64_t lba; uint32_t len; uint32_t transfer_len; uint32_t parameter_len; uint8_t *sense_data; int *sense_len; int lun_i; int rc; if (lu_cmd == NULL) return -1; lu = lu_cmd->lu; if (lu == NULL) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } spec = NULL; cdb = lu_cmd->cdb; data = lu_cmd->data; data_alloc_len = lu_cmd->alloc_len; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; lun_i = istgt_lu_islun2lun(lu_cmd->lun); if (lun_i >= lu->maxlun) { #ifdef ISTGT_TRACE_DISK ISTGT_ERRLOG("LU%d: LUN%4.4"PRIx64" invalid\n", lu->num, lun_i); #endif /* ISTGT_TRACE_DISK */ if (cdb[0] == SPC_INQUIRY) { allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], 0x03, 7, 3); BDADD8W(&data[0], 0x1f, 4, 5); data_len = 96; memset(&data[1], 0, data_len - 1); /* ADDITIONAL LENGTH */ data[4] = data_len - 5; lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } else { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } } spec = (ISTGT_LU_DISK *) lu->lun[lun_i].spec; if (spec == NULL) { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } if (spec->sense != 0) { int sk, asc, ascq; if (cdb[0] != SPC_INQUIRY && cdb[0] != SPC_REPORT_LUNS) { sk = (spec->sense >> 16) & 0xffU; asc = (spec->sense >> 8) & 0xffU; ascq = (spec->sense >> 0) & 0xffU; spec->sense = 0; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Generate sk=0x%x, asc=0x%x, ascq=0x%x\n", sk, asc, ascq); *sense_len = istgt_lu_disk_build_sense_data(spec, sense_data, sk, asc, ascq); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } } if (spec->err_write_cache) { /* WRITE ERROR - AUTO REALLOCATION FAILED */ BUILD_SENSE2(MEDIUM_ERROR, 0x0c, 0x02); #if 0 /* WRITE ERROR - RECOMMEND REASSIGNMENT */ BUILD_SENSE2(MEDIUM_ERROR, 0x0c, 0x03); #endif spec->err_write_cache = 0; lba = spec->woffset / spec->blocklen; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Deferred error (write cache) at %"PRIu64"\n", lba); if (lba > 0xffffffffULL) { ISTGT_WARNLOG("lba > 0xffffffff\n"); } /* COMMAND-SPECIFIC INFORMATION */ DSET32(&sense_data[8], (uint32_t)(lba & 0xffffffffULL)); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64"\n", cdb[0], lu_cmd->lun); #ifdef ISTGT_TRACE_DISK if (cdb[0] != SPC_TEST_UNIT_READY) { istgt_scsi_dump_cdb(cdb); } #endif /* ISTGT_TRACE_DISK */ switch (cdb[0]) { case SPC_INQUIRY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "INQUIRY\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_disk_scsi_inquiry(spec, conn, cdb, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "INQUIRY", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC_REPORT_LUNS: { int sel; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT LUNS\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); return -1; } sel = cdb[2]; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sel=%x\n", sel); allocation_len = DGET32(&cdb[6]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (allocation_len < 16) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } memset(data, 0, allocation_len); data_len = istgt_lu_disk_scsi_report_luns(lu, conn, cdb, sel, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "REPORT LUNS", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; } break; case SPC_TEST_UNIT_READY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "TEST_UNIT_READY\n"); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SBC_START_STOP_UNIT: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "START_STOP_UNIT\n"); { int pc, loej, start; pc = BGET8W(&cdb[4], 7, 4); loej = BGET8(&cdb[4], 1); start = BGET8(&cdb[4], 0); if (start != 0 || pc != 0) { if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; } break; case SBC_READ_CAPACITY_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_CAPACITY_10\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (spec->blockcnt - 1 > 0xffffffffULL) { DSET32(&data[0], 0xffffffffUL); } else { DSET32(&data[0], (uint32_t) (spec->blockcnt - 1)); } DSET32(&data[4], (uint32_t) spec->blocklen); data_len = 8; lu_cmd->data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SBC_READ_CAPACITY_10", data, data_len); break; case SPC_SERVICE_ACTION_IN_16: switch (BGET8W(&cdb[1], 4, 5)) { /* SERVICE ACTION */ case SBC_SAI_READ_CAPACITY_16: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_CAPACITY_16\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } allocation_len = DGET32(&cdb[10]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); DSET64(&data[0], spec->blockcnt - 1); DSET32(&data[8], (uint32_t) spec->blocklen); data[12] = 0; /* RTO_EN(1) PROT_EN(0) */ memset(&data[13], 0, 32 - (8 + 4 + 1)); /* Reserved */ data_len = 32; lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SBC_SAI_READ_LONG_16: default: /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } break; case SPC_MODE_SELECT_6: #if 0 istgt_scsi_dump_cdb(cdb); #endif { int pf, sp, pllen; int mdlen, mt, dsp, bdlen; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } pf = BGET8(&cdb[1], 4); sp = BGET8(&cdb[1], 0); pllen = cdb[4]; /* Parameter List Length */ if (pllen == 0) { lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } /* Data-Out */ rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, pllen); if (rc < 0) { ISTGT_ERRLOG("lu_disk_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (pllen < 4) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SELECT(6)", lu_cmd->iobuf, pllen); #endif data = lu_cmd->iobuf; mdlen = data[0]; /* Mode Data Length */ mt = data[1]; /* Medium Type */ dsp = data[2]; /* Device-Specific Parameter */ bdlen = data[3]; /* Block Descriptor Length */ /* Short LBA mode parameter block descriptor */ /* data[4]-data[7] Number of Blocks */ /* data[8]-data[11] Block Length */ /* page data */ data_len = istgt_lu_disk_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[4 + bdlen], pllen - (4 + bdlen)); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = pllen; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SELECT_10: #if 0 istgt_scsi_dump_cdb(cdb); #endif { int pf, sp, pllen; int mdlen, mt, dsp, bdlen; int llba; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } pf = BGET8(&cdb[1], 4); sp = BGET8(&cdb[1], 0); pllen = DGET16(&cdb[7]); /* Parameter List Length */ if (pllen == 0) { lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } /* Data-Out */ rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, pllen); if (rc < 0) { ISTGT_ERRLOG("lu_disk_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (pllen < 4) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SELECT(10)", lu_cmd->iobuf, pllen); #endif data = lu_cmd->iobuf; mdlen = DGET16(&data[0]); /* Mode Data Length */ mt = data[2]; /* Medium Type */ dsp = data[3]; /* Device-Specific Parameter */ llba = BGET8(&data[4], 0); /* Long LBA */ bdlen = DGET16(&data[6]); /* Block Descriptor Length */ if (llba) { /* Long LBA mode parameter block descriptor */ /* data[8]-data[15] Number of Blocks */ /* data[16]-data[19] Reserved */ /* data[20]-data[23] Block Length */ } else { /* Short LBA mode parameter block descriptor */ /* data[8]-data[11] Number of Blocks */ /* data[12]-data[15] Block Length */ } /* page data */ data_len = istgt_lu_disk_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[8 + bdlen], pllen - (8 + bdlen)); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = pllen; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SENSE_6: #if 0 istgt_scsi_dump_cdb(cdb); #endif { int dbd, pc, page, subpage; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dbd = BGET8(&cdb[1], 3); pc = BGET8W(&cdb[2], 7, 2); page = BGET8W(&cdb[2], 5, 6); subpage = cdb[3]; allocation_len = cdb[4]; if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_disk_scsi_mode_sense6(spec, conn, cdb, dbd, pc, page, subpage, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SENSE(6)", data, data_len); #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SENSE_10: #if 0 istgt_scsi_dump_cdb(cdb); #endif { int dbd, pc, page, subpage; int llbaa; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } llbaa = BGET8(&cdb[1], 4); dbd = BGET8(&cdb[1], 3); pc = BGET8W(&cdb[2], 7, 2); page = BGET8W(&cdb[2], 5, 6); subpage = cdb[3]; allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_disk_scsi_mode_sense10(spec, conn, cdb, llbaa, dbd, pc, page, subpage, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SENSE(10)", data, data_len); #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } #if 0 case SPC_LOG_SELECT: case SPC_LOG_SENSE: /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; #endif case SPC_REQUEST_SENSE: { int desc; int sk, asc, ascq; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } desc = BGET8(&cdb[1], 0); if (desc != 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } allocation_len = cdb[4]; if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); if (!spec->sense) { /* NO ADDITIONAL SENSE INFORMATION */ sk = ISTGT_SCSI_SENSE_NO_SENSE; asc = 0x00; ascq = 0x00; } else { sk = (spec->sense >> 16) & 0xffU; asc = (spec->sense >> 8) & 0xffU; ascq = spec->sense & 0xffU; } data_len = istgt_lu_disk_build_sense_data(spec, sense_data, sk, asc, ascq); if (data_len < 0 || data_len < 2) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* omit SenseLength */ data_len -= 2; memcpy(data, sense_data + 2, data_len); #if 0 istgt_dump("REQUEST SENSE", data, data_len); #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_READ_6: { if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lba = (uint64_t) (DGET24(&cdb[1]) & 0x001fffffU); transfer_len = (uint32_t) DGET8(&cdb[4]); if (transfer_len == 0) { transfer_len = 256; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_6(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbread() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_READ_10: { int dpo, fua, fua_nv; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); fua_nv = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); transfer_len = (uint32_t) DGET16(&cdb[7]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_10(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbread() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_READ_12: { int dpo, fua, fua_nv; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); fua_nv = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); transfer_len = (uint32_t) DGET32(&cdb[6]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_12(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbread() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_READ_16: { int dpo, fua, fua_nv; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); fua_nv = BGET8(&cdb[1], 1); lba = (uint64_t) DGET64(&cdb[2]); transfer_len = (uint32_t) DGET32(&cdb[10]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_16(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbread(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbread() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_WRITE_6: { if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lba = (uint64_t) (DGET24(&cdb[1]) & 0x001fffffU); transfer_len = (uint32_t) DGET8(&cdb[4]); if (transfer_len == 0) { transfer_len = 256; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_6(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbwrite() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_WRITE_10: case SBC_WRITE_AND_VERIFY_10: { int dpo, fua, fua_nv; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); fua_nv = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); transfer_len = (uint32_t) DGET16(&cdb[7]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_10(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbwrite() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_WRITE_12: case SBC_WRITE_AND_VERIFY_12: { int dpo, fua, fua_nv; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); fua_nv = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); transfer_len = (uint32_t) DGET32(&cdb[6]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_12(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbwrite() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_WRITE_16: case SBC_WRITE_AND_VERIFY_16: { int dpo, fua, fua_nv; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); fua_nv = BGET8(&cdb[1], 1); lba = (uint64_t) DGET64(&cdb[2]); transfer_len = (uint32_t) DGET32(&cdb[10]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_16(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbwrite(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbwrite() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_VERIFY_10: { int dpo, bytchk; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } dpo = BGET8(&cdb[1], 4); bytchk = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); len = (uint32_t) DGET16(&cdb[7]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "VERIFY_10(lba %"PRIu64", len %u blocks)\n", lba, len); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_VERIFY_12: { int dpo, bytchk; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } dpo = BGET8(&cdb[1], 4); bytchk = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); len = (uint32_t) DGET32(&cdb[6]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "VERIFY_12(lba %"PRIu64", len %u blocks)\n", lba, len); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_VERIFY_16: { int dpo, bytchk; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(1,0,1,1,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } dpo = BGET8(&cdb[1], 4); bytchk = BGET8(&cdb[1], 1); lba = (uint64_t) DGET64(&cdb[2]); len = (uint32_t) DGET32(&cdb[10]); ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "VERIFY_16(lba %"PRIu64", len %u blocks)\n", lba, len); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_WRITE_SAME_10: { int wprotect, pbdata, lbdata, group_no; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } wprotect = BGET8W(&cdb[1], 7, 3); pbdata = BGET8(&cdb[1], 2); lbdata = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); transfer_len = (uint32_t) DGET16(&cdb[7]); group_no = BGET8W(&cdb[6], 4, 5); /* only PBDATA=0 and LBDATA=0 support */ if (pbdata || lbdata) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_SAME_10(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbwrite_same(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbwrite_same() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_WRITE_SAME_16: { int wprotect, anchor, unmap, pbdata, lbdata, group_no; #if 0 istgt_scsi_dump_cdb(cdb); #endif if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } wprotect = BGET8W(&cdb[1], 7, 3); anchor = BGET8(&cdb[1], 4); unmap = BGET8(&cdb[1], 3); pbdata = BGET8(&cdb[1], 2); lbdata = BGET8(&cdb[1], 1); lba = (uint64_t) DGET64(&cdb[2]); transfer_len = (uint32_t) DGET32(&cdb[10]); group_no = BGET8W(&cdb[14], 4, 5); /* only PBDATA=0 and LBDATA=0 support */ if (pbdata || lbdata) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (anchor) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_SAME_16(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbwrite_same(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbwrite_same() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_COMPARE_AND_WRITE: { int64_t maxlen; int wprotect, dpo, fua, fua_nv, group_no; #if 0 istgt_scsi_dump_cdb(cdb); #endif if (spec->lu->istgt->swmode == ISTGT_SWMODE_TRADITIONAL) { /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } wprotect = BGET8W(&cdb[1], 7, 3); dpo = BGET8(&cdb[1], 4); fua = BGET8(&cdb[1], 3); fua_nv = BGET8(&cdb[1], 1); lba = (uint64_t) DGET64(&cdb[2]); transfer_len = (uint32_t) DGET8(&cdb[13]); group_no = BGET8W(&cdb[14], 4, 5); maxlen = ISTGT_LU_WORK_BLOCK_SIZE / spec->blocklen; if (maxlen > 0xff) { maxlen = 0xff; } if (transfer_len > maxlen) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "COMPARE_AND_WRITE(lba %"PRIu64", len %u blocks)\n", lba, transfer_len); rc = istgt_lu_disk_lbwrite_ats(spec, conn, lu_cmd, lba, transfer_len); if (rc < 0) { //ISTGT_ERRLOG("lu_disk_lbwrite_ats() failed\n"); /* sense data build by function */ lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_SYNCHRONIZE_CACHE_10: { int sync_nv, immed; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } sync_nv = BGET8(&cdb[1], 2); immed = BGET8(&cdb[1], 1); lba = (uint64_t) DGET32(&cdb[2]); len = (uint32_t) DGET16(&cdb[7]); if (len == 0) { len = spec->blockcnt; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SYNCHRONIZE_CACHE_10(lba %"PRIu64 ", len %u blocks)\n", lba, len); rc = istgt_lu_disk_lbsync(spec, conn, lu_cmd, lba, len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbsync() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SBC_SYNCHRONIZE_CACHE_16: { int sync_nv, immed; if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } sync_nv = BGET8(&cdb[1], 2); immed = BGET8(&cdb[1], 1); lba = (uint64_t) DGET64(&cdb[2]); len = (uint32_t) DGET32(&cdb[10]); if (len == 0) { len = spec->blockcnt; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SYNCHRONIZE_CACHE_10(lba %"PRIu64 ", len %u blocks)\n", lba, len); rc = istgt_lu_disk_lbsync(spec, conn, lu_cmd, lba, len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_lbsync() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SCC_MAINTENANCE_IN: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MAINTENANCE_IN\n"); switch (BGET8W(&cdb[1], 4, 5)) { /* SERVICE ACTION */ case SPC_MI_REPORT_TARGET_PORT_GROUPS: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT_TARGET_PORT_GROUPS\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } allocation_len = DGET32(&cdb[6]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_disk_scsi_report_target_port_groups(spec, conn, cdb, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "REPORT_TARGET_PORT_GROUPS", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; default: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SA=0x%2.2x\n", BGET8W(&cdb[1], 4, 5)); /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } break; case SCC_MAINTENANCE_OUT: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MAINTENANCE_OUT\n"); switch (BGET8W(&cdb[1], 4, 5)) { /* SERVICE ACTION */ case SPC_MO_SET_TARGET_PORT_GROUPS: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SET_TARGET_PORT_GROUPS\n"); if (spec->rsv_key) { rc = istgt_lu_disk_check_pr(spec, conn, PR_ALLOW(0,0,1,0,0)); if (rc != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_RESERVATION_CONFLICT; break; } } if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } parameter_len = DGET32(&cdb[6]); if (parameter_len == 0) { lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } /* Data-Out */ rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, parameter_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (parameter_len < 4) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SET_TARGET_PORT_GROUPS", lu_cmd->iobuf, parameter_len); data = lu_cmd->iobuf; /* data[0]-data[3] Reserved */ /* Set target port group descriptor(s) */ data_len = istgt_lu_disk_scsi_set_target_port_groups(spec, conn, cdb, &data[4], parameter_len - 4); if (data_len < 0) { /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = parameter_len; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; default: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SA=0x%2.2x\n", BGET8W(&cdb[1], 4, 5)); /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } break; case SPC_PERSISTENT_RESERVE_IN: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PERSISTENT_RESERVE_IN\n"); { int sa; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } sa = BGET8W(&cdb[1], 4, 5); allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_disk_scsi_persistent_reserve_in(spec, conn, lu_cmd, sa, data, allocation_len); if (data_len < 0) { /* status build by function */ break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "PERSISTENT_RESERVE_IN", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; } break; case SPC_PERSISTENT_RESERVE_OUT: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PERSISTENT_RESERVE_OUT\n"); { int sa, scope, type; if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } sa = BGET8W(&cdb[1], 4, 5); scope = BGET8W(&cdb[2], 7, 4); type = BGET8W(&cdb[2], 3, 4); parameter_len = DGET32(&cdb[5]); /* Data-Out */ rc = istgt_lu_disk_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, parameter_len); if (rc < 0) { ISTGT_ERRLOG("lu_disk_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (parameter_len < 24) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "PERSISTENT_RESERVE_OUT", lu_cmd->iobuf, parameter_len); data = lu_cmd->iobuf; data_len = istgt_lu_disk_scsi_persistent_reserve_out(spec, conn, lu_cmd, sa, scope, type, &data[0], parameter_len); if (data_len < 0) { /* status build by function */ break; } lu_cmd->data_len = parameter_len; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; } break; /* XXX TODO: fix */ case SPC_EXTENDED_COPY: /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; case SPC2_RELEASE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_6\n"); rc = istgt_lu_disk_scsi_release(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RELEASE_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_10\n"); rc = istgt_lu_disk_scsi_release(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RESERVE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_6\n"); rc = istgt_lu_disk_scsi_reserve(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RESERVE_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_10\n"); rc = istgt_lu_disk_scsi_reserve(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; default: ISTGT_ERRLOG("unsupported SCSI OP=0x%x\n", cdb[0]); /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64" status=0x%x," " complete\n", cdb[0], lu_cmd->lun, lu_cmd->status); return 0; } istgt-20111008/src/istgt_misc.h000644 000000 000000 00000020407 11373447313 016317 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_MISC_H #define ISTGT_MISC_H #include #include #include #define ISTGT_USE_MACRO_EXPAND #ifdef ISTGT_USE_MACRO_EXPAND #define DSET8(B,D) (*((uint8_t *)(B)) = (uint8_t)(D)) #define DSET16(B,D) \ (((*((uint8_t *)(B)+0)) = (uint8_t)((uint16_t)(D) >> 8)), \ ((*((uint8_t *)(B)+1)) = (uint8_t)((uint16_t)(D) >> 0))) #define DSET24(B,D) \ (((*((uint8_t *)(B)+0)) = (uint8_t)((uint32_t)(D) >> 16)), \ ((*((uint8_t *)(B)+1)) = (uint8_t)((uint32_t)(D) >> 8)), \ ((*((uint8_t *)(B)+2)) = (uint8_t)((uint32_t)(D) >> 0))) #define DSET32(B,D) \ (((*((uint8_t *)(B)+0)) = (uint8_t)((uint32_t)(D) >> 24)), \ ((*((uint8_t *)(B)+1)) = (uint8_t)((uint32_t)(D) >> 16)), \ ((*((uint8_t *)(B)+2)) = (uint8_t)((uint32_t)(D) >> 8)), \ ((*((uint8_t *)(B)+3)) = (uint8_t)((uint32_t)(D) >> 0))) #define DSET48(B,D) \ (((*((uint8_t *)(B)+0)) = (uint8_t)((uint64_t)(D) >> 40)), \ ((*((uint8_t *)(B)+1)) = (uint8_t)((uint64_t)(D) >> 32)), \ ((*((uint8_t *)(B)+2)) = (uint8_t)((uint64_t)(D) >> 24)), \ ((*((uint8_t *)(B)+3)) = (uint8_t)((uint64_t)(D) >> 16)), \ ((*((uint8_t *)(B)+4)) = (uint8_t)((uint64_t)(D) >> 8)), \ ((*((uint8_t *)(B)+5)) = (uint8_t)((uint64_t)(D) >> 0))) #define DSET64(B,D) \ (((*((uint8_t *)(B)+0)) = (uint8_t)((uint64_t)(D) >> 56)), \ ((*((uint8_t *)(B)+1)) = (uint8_t)((uint64_t)(D) >> 48)), \ ((*((uint8_t *)(B)+2)) = (uint8_t)((uint64_t)(D) >> 40)), \ ((*((uint8_t *)(B)+3)) = (uint8_t)((uint64_t)(D) >> 32)), \ ((*((uint8_t *)(B)+4)) = (uint8_t)((uint64_t)(D) >> 24)), \ ((*((uint8_t *)(B)+5)) = (uint8_t)((uint64_t)(D) >> 16)), \ ((*((uint8_t *)(B)+6)) = (uint8_t)((uint64_t)(D) >> 8)), \ ((*((uint8_t *)(B)+7)) = (uint8_t)((uint64_t)(D) >> 0))) #define DGET8(B) (*((uint8_t *)(B))) #define DGET16(B) \ ((( (uint16_t) *((uint8_t *)(B)+0)) << 8) \ | (((uint16_t) *((uint8_t *)(B)+1)) << 0)) #define DGET24(B) \ ((( (uint32_t) *((uint8_t *)(B)+0)) << 16) \ | (((uint32_t) *((uint8_t *)(B)+1)) << 8) \ | (((uint32_t) *((uint8_t *)(B)+2)) << 0)) #define DGET32(B) \ ((( (uint32_t) *((uint8_t *)(B)+0)) << 24) \ | (((uint32_t) *((uint8_t *)(B)+1)) << 16) \ | (((uint32_t) *((uint8_t *)(B)+2)) << 8) \ | (((uint32_t) *((uint8_t *)(B)+3)) << 0)) #define DGET48(B) \ ((( (uint64_t) *((uint8_t *)(B)+0)) << 40) \ | (((uint64_t) *((uint8_t *)(B)+1)) << 32) \ | (((uint64_t) *((uint8_t *)(B)+2)) << 24) \ | (((uint64_t) *((uint8_t *)(B)+3)) << 16) \ | (((uint64_t) *((uint8_t *)(B)+4)) << 8) \ | (((uint64_t) *((uint8_t *)(B)+5)) << 0)) #define DGET64(B) \ ((( (uint64_t) *((uint8_t *)(B)+0)) << 56) \ | (((uint64_t) *((uint8_t *)(B)+1)) << 48) \ | (((uint64_t) *((uint8_t *)(B)+2)) << 40) \ | (((uint64_t) *((uint8_t *)(B)+3)) << 32) \ | (((uint64_t) *((uint8_t *)(B)+4)) << 24) \ | (((uint64_t) *((uint8_t *)(B)+5)) << 16) \ | (((uint64_t) *((uint8_t *)(B)+6)) << 8) \ | (((uint64_t) *((uint8_t *)(B)+7)) << 0)) #else /* ISTGT_USE_MACRO_EXPAND */ //#define DSET8(B,D) (istgt_dset8((B),(D))) #define DSET8(B,D) (*((uint8_t *)(B)) = (uint8_t)(D)) #define DSET16(B,D) (istgt_dset16((B),(D))) #define DSET24(B,D) (istgt_dset24((B),(D))) #define DSET32(B,D) (istgt_dset32((B),(D))) #define DSET48(B,D) (istgt_dset48((B),(D))) #define DSET64(B,D) (istgt_dset64((B),(D))) //#define DGET8(B) (istgt_dget8((B))) #define DGET8(B) (*((uint8_t *)(B))) #define DGET16(B) (istgt_dget16((B))) #define DGET24(B) (istgt_dget24((B))) #define DGET32(B) (istgt_dget32((B))) #define DGET48(B) (istgt_dget48((B))) #define DGET64(B) (istgt_dget64((B))) #endif /* ISTGT_USE_MACRO_EXPAND */ #define DMIN8(A,B) ((uint8_t) ((A) > (B) ? (B) : (A))) #define DMIN16(A,B) ((uint16_t) ((A) > (B) ? (B) : (A))) #define DMIN24(A,B) ((uint32_t) ((A) > (B) ? (B) : (A))) #define DMIN32(A,B) ((uint32_t) ((A) > (B) ? (B) : (A))) #define DMIN48(A,B) ((uint64_t) ((A) > (B) ? (B) : (A))) #define DMIN64(A,B) ((uint64_t) ((A) > (B) ? (B) : (A))) #define DMAX8(A,B) ((uint8_t) ((A) > (B) ? (A) : (B))) #define DMAX16(A,B) ((uint16_t) ((A) > (B) ? (A) : (B))) #define DMAX24(A,B) ((uint32_t) ((A) > (B) ? (A) : (B))) #define DMAX32(A,B) ((uint32_t) ((A) > (B) ? (A) : (B))) #define DMAX48(A,B) ((uint64_t) ((A) > (B) ? (A) : (B))) #define DMAX64(A,B) ((uint64_t) ((A) > (B) ? (A) : (B))) #define BSHIFTNW(N,W) (((W) > 0) ? (((N) > ((W)-1)) ? ((N) - ((W)-1)) : 0) : 0) #define BMASKW(W) (((W) > 0) ? (~((~0) << (W))) : 0) #define BDSET8W(B,D,N,W) DSET8((B),(((D)&BMASKW((W)))<> BSHIFTNW((N),(W))) & BMASKW((W))) #define BDSET8(B,D,N) (BDSET8W((B),(D),(N),1)) #define BDADD8(B,D,N) (BDADD8W((B),(D),(N),1)) #define BSET8(B,N) (BSET8W((B),(N),1)) #define BCLR8(B,N) (BCLR8W((B),(N),1)) #define BGET8(B,N) (BGET8W((B),(N),1)) /* memory allocate */ void *xmalloc(size_t size); void *xrealloc(void *p, size_t size); void xfree(void *p); char *xstrdup(const char *s); /* string functions */ char *strlwr(char *s); char *strupr(char *s); char *strsepq(char **stringp, const char *delim); char *trim_string(char *s); char *escape_string(const char *s); #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size); #endif /* HAVE_STRLCPY */ /* convert from/to LBA/MSF */ uint32_t istgt_msf2lba(uint32_t msf); uint32_t istgt_lba2msf(uint32_t lba); /* network byte order operation */ uint8_t istgt_dget8(const uint8_t *data); void istgt_dset8(uint8_t *data, uint32_t value); uint16_t istgt_dget16(const uint8_t *data); void istgt_dset16(uint8_t *data, uint32_t value); uint32_t istgt_dget24(const uint8_t *data); void istgt_dset24(uint8_t *data, uint32_t value); uint32_t istgt_dget32(const uint8_t *data); void istgt_dset32(uint8_t *data, uint32_t value); uint64_t istgt_dget48(const uint8_t *data); void istgt_dset48(uint8_t *data, uint64_t value); uint64_t istgt_dget64(const uint8_t *data); void istgt_dset64(uint8_t *data, uint64_t value); /* random value generation */ void istgt_gen_random(uint8_t *buf, size_t len); #ifndef HAVE_SRANDOMDEV void srandomdev(void); #endif /* HAVE_SRANDOMDEV */ #ifndef HAVE_ARC4RANDOM uint32_t arc4random(void); #endif /* HAVE_ARC4RANDOM */ /* convert from/to bin/hex */ int istgt_bin2hex(char *buf, size_t len, const uint8_t *data, size_t data_len); int istgt_hex2bin(uint8_t *data, size_t data_len, const char *str); /* other functions */ void istgt_dump(const char *label, const uint8_t *buf, size_t len); void istgt_fdump(FILE *fp, const char *label, const uint8_t *buf, size_t len); void istgt_yield(void); #endif /* ISTGT_MISC_H */ istgt-20111008/src/istgt_lu_ctl.c000644 000000 000000 00000111241 11640123563 016631 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifdef HAVE_PTHREAD_NP_H #include #endif #include #include #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_md5.h" #include "istgt_lu.h" #include "istgt_iscsi.h" #include "istgt_proto.h" #define TIMEOUT_RW 60 #define MAX_LINEBUF 4096 typedef struct istgt_uctl_t { int id; ISTGT_Ptr istgt; PORTAL portal; int sock; pthread_t thread; int family; char caddr[MAX_ADDRBUF]; char saddr[MAX_ADDRBUF]; ISTGT_CHAP_AUTH auth; int authenticated; int timeout; int auth_group; int no_auth; int req_auth; int req_mutual; char *mediadirectory; int recvtmpsize; int recvtmpcnt; int recvtmpidx; int recvbufsize; int sendbufsize; int worksize; char recvtmp[MAX_LINEBUF]; char recvbuf[MAX_LINEBUF]; char sendbuf[MAX_LINEBUF]; char work[MAX_LINEBUF]; char *cmd; char *arg; } UCTL; typedef UCTL *UCTL_Ptr; typedef enum { UCTL_CMD_OK = 0, UCTL_CMD_ERR = 1, UCTL_CMD_EOF = 2, UCTL_CMD_QUIT = 3, UCTL_CMD_DISCON = 4, } UCTL_CMD_STATUS; #define ARGS_DELIM " \t" static int istgt_uctl_readline(UCTL_Ptr uctl) { ssize_t total; total = istgt_readline_socket(uctl->sock, uctl->recvbuf, uctl->recvbufsize, uctl->recvtmp, uctl->recvtmpsize, &uctl->recvtmpidx, &uctl->recvtmpcnt, uctl->timeout); if (total < 0) { return UCTL_CMD_DISCON; } if (total == 0) { return UCTL_CMD_EOF; } return UCTL_CMD_OK; } static int istgt_uctl_writeline(UCTL_Ptr uctl) { ssize_t total; ssize_t expect; expect = strlen(uctl->sendbuf); total = istgt_writeline_socket(uctl->sock, uctl->sendbuf, uctl->timeout); if (total < 0) { return UCTL_CMD_DISCON; } if (total != expect) { return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int istgt_uctl_snprintf(UCTL_Ptr uctl, const char *format, ...) { va_list ap; int rc; va_start(ap, format); rc = vsnprintf(uctl->sendbuf, uctl->sendbufsize, format, ap); va_end(ap); return rc; } static int istgt_uctl_get_media_present(ISTGT_LU_Ptr lu, int lun) { int rc; switch (lu->type) { case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_media_present(lu->lun[lun].spec); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_media_present(lu->lun[lun].spec); MTX_UNLOCK(&lu->mutex); break; default: rc = 0; } return rc; } static int istgt_uctl_get_media_lock(ISTGT_LU_Ptr lu, int lun) { int rc; switch (lu->type) { case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_media_lock(lu->lun[lun].spec); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_media_lock(lu->lun[lun].spec); MTX_UNLOCK(&lu->mutex); break; default: rc = 0; } return rc; } static int istgt_uctl_get_authinfo(UCTL_Ptr uctl, const char *authuser) { char *authfile = NULL; int ag_tag; int rc; ag_tag = uctl->auth_group; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ag_tag=%d\n", ag_tag); MTX_LOCK(&uctl->istgt->mutex); authfile = xstrdup(uctl->istgt->authfile); MTX_UNLOCK(&uctl->istgt->mutex); rc = istgt_chap_get_authinfo(&uctl->auth, authfile, authuser, ag_tag); if (rc < 0) { ISTGT_ERRLOG("chap_get_authinfo() failed\n"); xfree(authfile); return -1; } xfree(authfile); return 0; } static int istgt_uctl_cmd_auth(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *label; char *chap_a; char *chap_i; char *chap_c; char *chap_n; char *chap_r; int rc; arg = uctl->arg; label = strsepq(&arg, delim); if (label == NULL) { istgt_uctl_snprintf(uctl, "ERR invalid parameters\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (strcasecmp(label, "CHAP_A") == 0) { if (uctl->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_A) { istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n"); error_return: uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A; rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } chap_a = strsepq(&arg, delim); if (chap_a == NULL || strcasecmp(chap_a, "5") != 0) { istgt_uctl_snprintf(uctl, "ERR invalid algorithm\n"); goto error_return; } /* Identifier is one octet */ istgt_gen_random(uctl->auth.chap_id, 1); /* Challenge Value is a variable stream of octets */ /* (binary length MUST not exceed 1024 bytes) */ uctl->auth.chap_challenge_len = ISTGT_CHAP_CHALLENGE_LEN; istgt_gen_random(uctl->auth.chap_challenge, uctl->auth.chap_challenge_len); istgt_bin2hex(uctl->work, uctl->worksize, uctl->auth.chap_challenge, uctl->auth.chap_challenge_len); istgt_uctl_snprintf(uctl, "%s CHAP_IC %d %s\n", uctl->cmd, (int) uctl->auth.chap_id[0], uctl->work); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_NR; /* 3-way handshake */ return UCTL_CMD_OK; } else if (strcasecmp(label, "CHAP_NR") == 0) { uint8_t resmd5[ISTGT_MD5DIGEST_LEN]; uint8_t tgtmd5[ISTGT_MD5DIGEST_LEN]; ISTGT_MD5CTX md5ctx; if (uctl->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_NR) { istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n"); goto error_return; } chap_n = strsepq(&arg, delim); chap_r = strsepq(&arg, delim); if (chap_n == NULL || chap_r == NULL) { istgt_uctl_snprintf(uctl, "ERR no response\n"); goto error_return; } //ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "N=%s, R=%s\n", chap_n, chap_r); rc = istgt_hex2bin(resmd5, ISTGT_MD5DIGEST_LEN, chap_r); if (rc < 0 || rc != ISTGT_MD5DIGEST_LEN) { istgt_uctl_snprintf(uctl, "ERR response format error\n"); goto error_return; } rc = istgt_uctl_get_authinfo(uctl, chap_n); if (rc < 0) { ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n); istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n"); goto error_return; } if (uctl->auth.user == NULL || uctl->auth.secret == NULL) { ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n); istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n"); goto error_return; } istgt_md5init(&md5ctx); /* Identifier */ istgt_md5update(&md5ctx, uctl->auth.chap_id, 1); /* followed by secret */ istgt_md5update(&md5ctx, uctl->auth.secret, strlen(uctl->auth.secret)); /* followed by Challenge Value */ istgt_md5update(&md5ctx, uctl->auth.chap_challenge, uctl->auth.chap_challenge_len); /* tgtmd5 is expecting Response Value */ istgt_md5final(tgtmd5, &md5ctx); /* compare MD5 digest */ if (memcmp(tgtmd5, resmd5, ISTGT_MD5DIGEST_LEN) != 0) { /* not match */ ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n); istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n"); goto error_return; } /* OK client's secret */ uctl->authenticated = 1; /* mutual CHAP? */ chap_i = strsepq(&arg, delim); chap_c = strsepq(&arg, delim); if (chap_i != NULL && chap_c != NULL) { /* Identifier */ uctl->auth.chap_mid[0] = (uint8_t) strtol(chap_i, NULL, 10); /* Challenge Value */ rc = istgt_hex2bin(uctl->auth.chap_mchallenge, ISTGT_CHAP_CHALLENGE_LEN, chap_c); if (rc < 0) { istgt_uctl_snprintf(uctl, "ERR challenge format error\n"); goto error_return; } uctl->auth.chap_mchallenge_len = rc; if (uctl->auth.muser == NULL || uctl->auth.msecret == NULL) { ISTGT_ERRLOG("auth failed (user %.64s)\n", chap_n); istgt_uctl_snprintf(uctl, "ERR auth user or secret is missing\n"); goto error_return; } istgt_md5init(&md5ctx); /* Identifier */ istgt_md5update(&md5ctx, uctl->auth.chap_mid, 1); /* followed by secret */ istgt_md5update(&md5ctx, uctl->auth.msecret, strlen(uctl->auth.msecret)); /* followed by Challenge Value */ istgt_md5update(&md5ctx, uctl->auth.chap_mchallenge, uctl->auth.chap_mchallenge_len); /* tgtmd5 is Response Value */ istgt_md5final(tgtmd5, &md5ctx); istgt_bin2hex(uctl->work, uctl->worksize, tgtmd5, ISTGT_MD5DIGEST_LEN); /* send NR for mutual CHAP */ istgt_uctl_snprintf(uctl, "%s CHAP_NR \"%s\" %s\n", uctl->cmd, uctl->auth.muser, uctl->work); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } } else { /* not mutual */ if (uctl->req_mutual) { ISTGT_ERRLOG("required mutual CHAP\n"); istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n"); goto error_return; } } uctl->auth.chap_phase = ISTGT_CHAP_PHASE_END; } else { istgt_uctl_snprintf(uctl, "ERR CHAP sequence error\n"); goto error_return; } /* auth succeeded (but mutual may fail) */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_quit(UCTL_Ptr uctl) { int rc; istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_QUIT; } static int istgt_uctl_cmd_noop(UCTL_Ptr uctl) { int rc; istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_version(UCTL_Ptr uctl) { int rc; istgt_uctl_snprintf(uctl, "%s %s (%s)\n", uctl->cmd, ISTGT_VERSION, ISTGT_EXTRA_VERSION); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* version succeeded */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_list(UCTL_Ptr uctl) { ISTGT_LU_Ptr lu; ISTGT_LU_LUN_Ptr llp; const char *delim = ARGS_DELIM; char *arg; char *iqn; char *lun; char *mflags; char *mfile; char *msize; char *mtype; char *workp; int lun_i; int worksize; int present; int lock; int rc; int i; arg = uctl->arg; iqn = strsepq(&arg, delim); lun = strsepq(&arg, delim); if (arg != NULL) { istgt_uctl_snprintf(uctl, "ERR invalid parameters\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (iqn == NULL) { /* all targets */ MTX_LOCK(&uctl->istgt->mutex); for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = uctl->istgt->logical_unit[i]; if (lu == NULL) continue; istgt_uctl_snprintf(uctl, "%s %s\n", uctl->cmd, lu->name); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { MTX_UNLOCK(&uctl->istgt->mutex); return rc; } } MTX_UNLOCK(&uctl->istgt->mutex); } else { /* specified target */ MTX_LOCK(&uctl->istgt->mutex); if (lun == NULL) { lun_i = 0; } else { lun_i = (int) strtol(lun, NULL, 10); } lu = istgt_lu_find_target(uctl->istgt, iqn); if (lu == NULL) { MTX_UNLOCK(&uctl->istgt->mutex); istgt_uctl_snprintf(uctl, "ERR no target\n"); error_return: rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun_i < 0 || lun_i >= lu->maxlun) { MTX_UNLOCK(&uctl->istgt->mutex); istgt_uctl_snprintf(uctl, "ERR no target\n"); goto error_return; } llp = &lu->lun[lun_i]; worksize = uctl->worksize; workp = uctl->work; switch (llp->type) { case ISTGT_LU_LUN_TYPE_REMOVABLE: mflags = istgt_lu_get_media_flags_string(llp->u.removable.flags, workp, worksize); worksize -= strlen(mflags) + 1; workp += strlen(mflags) + 1; present = istgt_uctl_get_media_present(lu, lun_i); lock = istgt_uctl_get_media_lock(lu, lun_i); mfile = llp->u.removable.file; if (llp->u.removable.flags & ISTGT_LU_FLAG_MEDIA_AUTOSIZE) { snprintf(workp, worksize, "auto"); } else { snprintf(workp, worksize, "%"PRIu64, llp->u.removable.size); } msize = workp; worksize -= strlen(msize) + 1; workp += strlen(msize) + 1; snprintf(workp, worksize, "-"); mtype = workp; worksize -= strlen(msize) + 1; workp += strlen(msize) + 1; istgt_uctl_snprintf(uctl, "%s lun%u %s %s %s %s %s \"%s\" %s\n", uctl->cmd, lun_i, "removable", (present ? "present" : "absent"), (lock ? "lock" : "unlock"), mtype, mflags, mfile, msize); rc = istgt_uctl_writeline(uctl); break; case ISTGT_LU_LUN_TYPE_STORAGE: mfile = llp->u.storage.file; snprintf(workp, worksize, "%"PRIu64, llp->u.storage.size); msize = workp; worksize -= strlen(msize) + 1; workp += strlen(msize) + 1; istgt_uctl_snprintf(uctl, "%s lun%u %s \"%s\" %s\n", uctl->cmd, lun_i, "storage", mfile, msize); rc = istgt_uctl_writeline(uctl); break; case ISTGT_LU_LUN_TYPE_DEVICE: mfile = llp->u.device.file; istgt_uctl_snprintf(uctl, "%s lun%u %s \"%s\"\n", uctl->cmd, lun_i, "device", mfile); rc = istgt_uctl_writeline(uctl); break; case ISTGT_LU_LUN_TYPE_SLOT: default: MTX_UNLOCK(&uctl->istgt->mutex); istgt_uctl_snprintf(uctl, "ERR unsupport LUN type\n"); goto error_return; } if (rc != UCTL_CMD_OK) { MTX_UNLOCK(&uctl->istgt->mutex); return rc; } MTX_UNLOCK(&uctl->istgt->mutex); } /* list succeeded */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_unload(UCTL_Ptr uctl) { ISTGT_LU_Ptr lu; ISTGT_LU_LUN_Ptr llp; const char *delim = ARGS_DELIM; char *arg; char *iqn; char *lun; int lun_i; int rc; arg = uctl->arg; iqn = strsepq(&arg, delim); lun = strsepq(&arg, delim); if (iqn == NULL || arg != NULL) { istgt_uctl_snprintf(uctl, "ERR invalid parameters\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun == NULL) { lun_i = 0; } else { lun_i = (int) strtol(lun, NULL, 10); } lu = istgt_lu_find_target(uctl->istgt, iqn); if (lu == NULL) { istgt_uctl_snprintf(uctl, "ERR no target\n"); error_return: rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun_i < 0 || lun_i >= lu->maxlun) { istgt_uctl_snprintf(uctl, "ERR no target\n"); goto error_return; } llp = &lu->lun[lun_i]; if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) { istgt_uctl_snprintf(uctl, "ERR not removable\n"); goto error_return; } /* unload media from lun */ switch (lu->type) { case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_unload_media(lu->lun[lun_i].spec); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_unload_media(lu->lun[lun_i].spec); MTX_UNLOCK(&lu->mutex); break; default: rc = -1; } if (rc < 0) { istgt_uctl_snprintf(uctl, "ERR unload\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } /* logging event */ ISTGT_NOTICELOG("Media Unload %s lun%d from %s\n", iqn, lun_i, uctl->caddr); /* unload succeeded */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_load(UCTL_Ptr uctl) { ISTGT_LU_Ptr lu; ISTGT_LU_LUN_Ptr llp; const char *delim = ARGS_DELIM; char *arg; char *iqn; char *lun; int lun_i; int rc; arg = uctl->arg; iqn = strsepq(&arg, delim); lun = strsepq(&arg, delim); if (iqn == NULL || arg != NULL) { istgt_uctl_snprintf(uctl, "ERR invalid parameters\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun == NULL) { lun_i = 0; } else { lun_i = (int) strtol(lun, NULL, 10); } lu = istgt_lu_find_target(uctl->istgt, iqn); if (lu == NULL) { istgt_uctl_snprintf(uctl, "ERR no target\n"); error_return: rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun_i < 0 || lun_i >= lu->maxlun) { istgt_uctl_snprintf(uctl, "ERR no target\n"); goto error_return; } llp = &lu->lun[lun_i]; if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) { istgt_uctl_snprintf(uctl, "ERR not removable\n"); goto error_return; } /* load media to lun */ switch (lu->type) { case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_load_media(lu->lun[lun_i].spec); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_load_media(lu->lun[lun_i].spec); MTX_UNLOCK(&lu->mutex); break; default: rc = -1; } if (rc < 0) { istgt_uctl_snprintf(uctl, "ERR load\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } /* logging event */ ISTGT_NOTICELOG("Media Load %s lun%d from %s\n", iqn, lun_i, uctl->caddr); /* load succeeded */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_change(UCTL_Ptr uctl) { ISTGT_LU_Ptr lu; ISTGT_LU_LUN_Ptr llp; const char *delim = ARGS_DELIM; char empty_flags[] = "ro"; char empty_size[] = "0"; char *arg; char *iqn; char *lun; char *type; char *flags; char *file; char *size; char *safedir; char *fullpath; char *abspath; int lun_i; int len; int rc; arg = uctl->arg; iqn = strsepq(&arg, delim); lun = strsepq(&arg, delim); type = strsepq(&arg, delim); flags = strsepq(&arg, delim); file = strsepq(&arg, delim); size = strsepq(&arg, delim); if (iqn == NULL || lun == NULL || type == NULL || flags == NULL || file == NULL || size == NULL || arg != NULL) { istgt_uctl_snprintf(uctl, "ERR invalid parameters\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun == NULL) { lun_i = 0; } else { lun_i = (int) strtol(lun, NULL, 10); } lu = istgt_lu_find_target(uctl->istgt, iqn); if (lu == NULL) { istgt_uctl_snprintf(uctl, "ERR no target\n"); error_return: rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun_i < 0 || lun_i >= lu->maxlun) { istgt_uctl_snprintf(uctl, "ERR no target\n"); goto error_return; } llp = &lu->lun[lun_i]; if (llp->type != ISTGT_LU_LUN_TYPE_REMOVABLE) { istgt_uctl_snprintf(uctl, "ERR not removable\n"); goto error_return; } /* make safe directory (start '/', end '/') */ len = 1 + strlen(uctl->mediadirectory) + 1 + 1; safedir = xmalloc(len); if (uctl->mediadirectory[0] != '/') { ISTGT_WARNLOG("MediaDirectory is not starting with '/'\n"); snprintf(safedir, len, "/%s", uctl->mediadirectory); } else { snprintf(safedir, len, "%s", uctl->mediadirectory); } if (strlen(safedir) > 1 && safedir[strlen(safedir) - 1] != '/') { safedir[strlen(safedir) + 1] = '\0'; safedir[strlen(safedir)] = '/'; } /* check abspath in mediadirectory? */ len = strlen(safedir) + strlen(file) + 1; fullpath = xmalloc(len); if (file[0] != '/') { snprintf(fullpath, len, "%s%s", safedir, file); } else { snprintf(fullpath, len, "%s", file); } #ifdef PATH_MAX abspath = xmalloc(len + PATH_MAX); file = realpath(fullpath, abspath); #else /* { long path_max; path_max = pathconf(fullpath, _PC_PATH_MAX); if (path_max != -1L) { abspath = xmalloc(path_max); file = realpath(fullpath, abspath); } } */ file = abspath = realpath(fullpath, NULL); #endif /* PATH_MAX */ if (file == NULL) { ISTGT_ERRLOG("realpath(%s) failed\n", fullpath); internal_error: xfree(safedir); xfree(fullpath); xfree(abspath); istgt_uctl_snprintf(uctl, "ERR %s internal error\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (strcasecmp(file, "/dev/null") == 0) { /* OK, empty slot */ flags = empty_flags; size = empty_size; } else if (strncasecmp(file, safedir, strlen(safedir)) != 0) { ISTGT_ERRLOG("Realpath(%s) is not within MediaDirectory(%s)\n", file, safedir); goto internal_error; } /* unload and load media from lun */ switch (lu->type) { case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_change_media(lu->lun[lun_i].spec, type, flags, file, size); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_change_media(lu->lun[lun_i].spec, type, flags, file, size); MTX_UNLOCK(&lu->mutex); break; default: rc = -1; } if (rc < 0) { xfree(safedir); xfree(fullpath); xfree(abspath); istgt_uctl_snprintf(uctl, "ERR change\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } /* logging event */ ISTGT_NOTICELOG("Media Change \"%s %s %s %s\" on %s lun%d from %s\n", type, flags, file, size, iqn, lun_i, uctl->caddr); xfree(safedir); xfree(fullpath); xfree(abspath); /* change succeeded */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_reset(UCTL_Ptr uctl) { ISTGT_LU_Ptr lu; ISTGT_LU_LUN_Ptr llp; const char *delim = ARGS_DELIM; char *arg; char *iqn; char *lun; int lun_i; int rc; arg = uctl->arg; iqn = strsepq(&arg, delim); lun = strsepq(&arg, delim); if (iqn == NULL || arg != NULL) { istgt_uctl_snprintf(uctl, "ERR invalid parameters\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun == NULL) { lun_i = 0; } else { lun_i = (int) strtol(lun, NULL, 10); } lu = istgt_lu_find_target(uctl->istgt, iqn); if (lu == NULL) { istgt_uctl_snprintf(uctl, "ERR no target\n"); error_return: rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } if (lun_i < 0 || lun_i >= lu->maxlun) { istgt_uctl_snprintf(uctl, "ERR no target\n"); goto error_return; } llp = &lu->lun[lun_i]; /* reset lun */ switch (lu->type) { case ISTGT_LU_TYPE_DISK: MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_reset(lu, lun_i); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_reset(lu, lun_i); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_reset(lu, lun_i); MTX_UNLOCK(&lu->mutex); break; case ISTGT_LU_TYPE_NONE: case ISTGT_LU_TYPE_PASS: rc = -1; break; default: rc = -1; } if (rc < 0) { istgt_uctl_snprintf(uctl, "ERR reset\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } /* logging event */ ISTGT_NOTICELOG("Unit Reset %s lun%d from %s\n", iqn, lun_i, uctl->caddr); /* reset succeeded */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } static int istgt_uctl_cmd_info(UCTL_Ptr uctl) { ISTGT_LU_Ptr lu; CONN_Ptr conn; SESS_Ptr sess; const char *delim = ARGS_DELIM; char *arg; char *iqn; int ncount; int rc; int i, j, k; arg = uctl->arg; iqn = strsepq(&arg, delim); if (arg != NULL) { istgt_uctl_snprintf(uctl, "ERR invalid parameters\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_ERR; } ncount = 0; MTX_LOCK(&uctl->istgt->mutex); for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = uctl->istgt->logical_unit[i]; if (lu == NULL) continue; if (iqn != NULL && strcasecmp(iqn, lu->name) != 0) continue; istgt_lock_gconns(); MTX_LOCK(&lu->mutex); for (j = 1; j < MAX_LU_TSIH; j++) { if (lu->tsih[j].initiator_port != NULL && lu->tsih[j].tsih != 0) { conn = istgt_find_conn(lu->tsih[j].initiator_port, lu->name, lu->tsih[j].tsih); if (conn == NULL || conn->sess == NULL) continue; sess = conn->sess; MTX_LOCK(&sess->mutex); for (k = 0; k < sess->connections; k++) { conn = sess->conns[k]; if (conn == NULL) continue; istgt_uctl_snprintf(uctl, "%s Login from %s (%s) on %s LU%d" " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u," " CID=%u, HeaderDigest=%s, DataDigest=%s," " MaxConnections=%u," " FirstBurstLength=%u, MaxBurstLength=%u," " MaxRecvDataSegmentLength=%u," " InitialR2T=%s, ImmediateData=%s\n", uctl->cmd, conn->initiator_name, conn->initiator_addr, conn->target_name, lu->num, conn->portal.host, conn->portal.port, conn->portal.tag, conn->sess->isid, conn->sess->tsih, conn->cid, (conn->header_digest ? "on" : "off"), (conn->data_digest ? "on" : "off"), conn->sess->MaxConnections, conn->sess->FirstBurstLength, conn->sess->MaxBurstLength, conn->MaxRecvDataSegmentLength, (conn->sess->initial_r2t ? "Yes" : "No"), (conn->sess->immediate_data ? "Yes" : "No")); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { MTX_UNLOCK(&sess->mutex); MTX_UNLOCK(&lu->mutex); istgt_unlock_gconns(); MTX_UNLOCK(&uctl->istgt->mutex); return rc; } ncount++; } MTX_UNLOCK(&sess->mutex); } } MTX_UNLOCK(&lu->mutex); istgt_unlock_gconns(); } MTX_UNLOCK(&uctl->istgt->mutex); if (ncount == 0) { istgt_uctl_snprintf(uctl, "%s no login\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } } /* info succeeded */ istgt_uctl_snprintf(uctl, "OK %s\n", uctl->cmd); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } return UCTL_CMD_OK; } typedef struct istgt_uctl_cmd_table_t { const char *name; int (*func) (UCTL_Ptr uctl); } ISTGT_UCTL_CMD_TABLE; static ISTGT_UCTL_CMD_TABLE istgt_uctl_cmd_table[] = { { "AUTH", istgt_uctl_cmd_auth }, { "QUIT", istgt_uctl_cmd_quit }, { "NOOP", istgt_uctl_cmd_noop }, { "VERSION", istgt_uctl_cmd_version }, { "LIST", istgt_uctl_cmd_list }, { "UNLOAD", istgt_uctl_cmd_unload }, { "LOAD", istgt_uctl_cmd_load }, { "CHANGE", istgt_uctl_cmd_change }, { "RESET", istgt_uctl_cmd_reset }, { "INFO", istgt_uctl_cmd_info }, { NULL, NULL }, }; static int istgt_uctl_cmd_execute(UCTL_Ptr uctl) { int (*func) (UCTL_Ptr); const char *delim = ARGS_DELIM; char *arg; char *cmd; int rc; int i; arg = trim_string(uctl->recvbuf); cmd = strsepq(&arg, delim); uctl->arg = arg; uctl->cmd = strupr(cmd); func = NULL; for (i = 0; istgt_uctl_cmd_table[i].name != NULL; i++) { if (cmd[0] == istgt_uctl_cmd_table[i].name[0] && strcmp(cmd, istgt_uctl_cmd_table[i].name) == 0) { func = istgt_uctl_cmd_table[i].func; break; } } if (func == NULL) { istgt_uctl_snprintf(uctl, "ERR unknown command\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return UCTL_CMD_DISCON; } return UCTL_CMD_ERR; } if (uctl->no_auth && (strcasecmp(cmd, "AUTH") == 0)) { istgt_uctl_snprintf(uctl, "ERR auth not required\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return UCTL_CMD_DISCON; } return UCTL_CMD_ERR; } if (uctl->req_auth && uctl->authenticated == 0 && !(strcasecmp(cmd, "QUIT") == 0 || strcasecmp(cmd, "AUTH") == 0)) { istgt_uctl_snprintf(uctl, "ERR auth required\n"); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return UCTL_CMD_DISCON; } return UCTL_CMD_ERR; } rc = func(uctl); return rc; } static void istgt_free_uctl(UCTL_Ptr uctl); static void * uctlworker(void *arg) { UCTL_Ptr uctl = (UCTL_Ptr) arg; int rc; ISTGT_TRACELOG(ISTGT_TRACE_NET, "connect to %s:%s,%d\n", uctl->portal.host, uctl->portal.port, uctl->portal.tag); istgt_uctl_snprintf(uctl, "iSCSI Target Controller version %s (%s)" " on %s from %s\n", ISTGT_VERSION, ISTGT_EXTRA_VERSION, uctl->saddr, uctl->caddr); rc = istgt_uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { ISTGT_ERRLOG("uctl_writeline() failed\n"); return NULL; } while (1) { if (istgt_get_state(uctl->istgt) != ISTGT_STATE_RUNNING) { break; } /* read from socket */ rc = istgt_uctl_readline(uctl); if (rc == UCTL_CMD_EOF) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "uctl_readline() EOF\n"); break; } if (rc != UCTL_CMD_OK) { ISTGT_ERRLOG("uctl_readline() failed\n"); break; } /* execute command */ rc = istgt_uctl_cmd_execute(uctl); if (rc == UCTL_CMD_QUIT) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "receive QUIT\n"); break; } if (rc == UCTL_CMD_DISCON) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request disconnect\n"); break; } } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "exiting ctlworker\n"); close(uctl->sock); uctl->sock = -1; istgt_free_uctl(uctl); return NULL; } static void istgt_free_uctl(UCTL_Ptr uctl) { if (uctl == NULL) return; xfree(uctl->mediadirectory); xfree(uctl->portal.label); xfree(uctl->portal.host); xfree(uctl->portal.port); xfree(uctl->auth.user); xfree(uctl->auth.secret); xfree(uctl->auth.muser); xfree(uctl->auth.msecret); xfree(uctl); } int istgt_create_uctl(ISTGT_Ptr istgt, PORTAL_Ptr portal, int sock, struct sockaddr *sa, socklen_t salen) { char buf[MAX_TMPBUF]; UCTL_Ptr uctl; int rc; int i; uctl = xmalloc(sizeof *uctl); memset(uctl, 0, sizeof *uctl); uctl->istgt = istgt; MTX_LOCK(&istgt->mutex); uctl->auth_group = istgt->uctl_auth_group; uctl->no_auth = istgt->no_uctl_auth; uctl->req_auth = istgt->req_uctl_auth; uctl->req_mutual = istgt->req_uctl_auth_mutual; uctl->mediadirectory = xstrdup(istgt->mediadirectory); MTX_UNLOCK(&istgt->mutex); uctl->portal.label = xstrdup(portal->label); uctl->portal.host = xstrdup(portal->host); uctl->portal.port = xstrdup(portal->port); uctl->portal.tag = portal->tag; uctl->portal.sock = -1; uctl->sock = sock; uctl->timeout = TIMEOUT_RW; uctl->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A; uctl->auth.user = NULL; uctl->auth.secret = NULL; uctl->auth.muser = NULL; uctl->auth.msecret = NULL; uctl->authenticated = 0; uctl->recvtmpcnt = 0; uctl->recvtmpidx = 0; uctl->recvtmpsize = sizeof uctl->recvtmp; uctl->recvbufsize = sizeof uctl->recvbuf; uctl->sendbufsize = sizeof uctl->sendbuf; uctl->worksize = sizeof uctl->work; memset(uctl->caddr, 0, sizeof uctl->caddr); memset(uctl->saddr, 0, sizeof uctl->saddr); switch (sa->sa_family) { case AF_INET6: uctl->family = AF_INET6; rc = istgt_getaddr(sock, uctl->saddr, sizeof uctl->saddr, uctl->caddr, sizeof uctl->caddr); if (rc < 0) { ISTGT_ERRLOG("istgt_getaddr() failed\n"); goto error_return; } break; case AF_INET: uctl->family = AF_INET; rc = istgt_getaddr(sock, uctl->saddr, sizeof uctl->saddr, uctl->caddr, sizeof uctl->caddr); if (rc < 0) { ISTGT_ERRLOG("istgt_getaddr() failed\n"); goto error_return; } break; default: ISTGT_ERRLOG("unsupported family\n"); goto error_return; } if (istgt->nuctl_netmasks != 0) { rc = -1; for (i = 0; i < istgt->nuctl_netmasks; i++) { rc = istgt_lu_allow_netmask(istgt->uctl_netmasks[i], uctl->caddr); if (rc > 0) { /* OK netmask */ break; } } if (rc <= 0) { ISTGT_WARNLOG("UCTL access denied from %s to (%s:%s)\n", uctl->caddr, uctl->portal.host, uctl->portal.port); goto error_return; } } printf("sock=%d, addr=%s, peer=%s\n", sock, uctl->saddr, uctl->caddr); /* wildcard? */ if (strcasecmp(uctl->portal.host, "[::]") == 0 || strcasecmp(uctl->portal.host, "[*]") == 0) { if (uctl->family != AF_INET6) { ISTGT_ERRLOG("address family error\n"); goto error_return; } snprintf(buf, sizeof buf, "[%s]", uctl->caddr); xfree(uctl->portal.host); uctl->portal.host = xstrdup(buf); } else if (strcasecmp(uctl->portal.host, "0.0.0.0") == 0 || strcasecmp(uctl->portal.host, "*") == 0) { if (uctl->family != AF_INET) { ISTGT_ERRLOG("address family error\n"); goto error_return; } snprintf(buf, sizeof buf, "%s", uctl->caddr); xfree(uctl->portal.host); uctl->portal.host = xstrdup(buf); } /* set timeout msec. */ rc = istgt_set_recvtimeout(uctl->sock, uctl->timeout * 1000); if (rc != 0) { ISTGT_ERRLOG("istgt_set_recvtimeo() failed\n"); goto error_return; } rc = istgt_set_sendtimeout(uctl->sock, uctl->timeout * 1000); if (rc != 0) { ISTGT_ERRLOG("istgt_set_sendtimeo() failed\n"); goto error_return; } /* create new thread */ #ifdef ISTGT_STACKSIZE rc = pthread_create(&uctl->thread, &istgt->attr, &uctlworker, (void *)uctl); #else rc = pthread_create(&uctl->thread, NULL, &uctlworker, (void *)uctl); #endif if (rc != 0) { ISTGT_ERRLOG("pthread_create() failed\n"); error_return: xfree(uctl->portal.label); xfree(uctl->portal.host); xfree(uctl->portal.port); xfree(uctl); return -1; } rc = pthread_detach(uctl->thread); if (rc != 0) { ISTGT_ERRLOG("pthread_detach() failed\n"); goto error_return; } #ifdef HAVE_PTHREAD_SET_NAME_NP pthread_set_name_np(uctl->thread, "uctlthread"); #endif return 0; } int istgt_init_uctl(ISTGT_Ptr istgt) { CF_SECTION *sp; const char *val; const char *ag_tag; int alloc_len; int ag_tag_i; int masks; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_init_uctl_section\n"); sp = istgt_find_cf_section(istgt->config, "UnitControl"); if (sp == NULL) { ISTGT_ERRLOG("find_cf_section failed()\n"); return -1; } val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Netmask", i); if (val == NULL) break; } masks = i; if (masks > MAX_NETMASK) { ISTGT_ERRLOG("%d > MAX_NETMASK\n", masks); return -1; } istgt->nuctl_netmasks = masks; alloc_len = sizeof (char *) * masks; istgt->uctl_netmasks = xmalloc(alloc_len); for (i = 0; i < masks; i++) { val = istgt_get_nval(sp, "Netmask", i); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Netmask %s\n", val); istgt->uctl_netmasks[i] = xstrdup(val); } val = istgt_get_val(sp, "AuthMethod"); if (val == NULL) { istgt->no_uctl_auth = 0; istgt->req_uctl_auth = 0; } else { istgt->no_uctl_auth = 0; for (i = 0; ; i++) { val = istgt_get_nmval(sp, "AuthMethod", 0, i); if (val == NULL) break; if (strcasecmp(val, "CHAP") == 0) { istgt->req_uctl_auth = 1; } else if (strcasecmp(val, "Mutual") == 0) { istgt->req_uctl_auth_mutual = 1; } else if (strcasecmp(val, "Auto") == 0) { istgt->req_uctl_auth = 0; istgt->req_uctl_auth_mutual = 0; } else if (strcasecmp(val, "None") == 0) { istgt->no_uctl_auth = 1; istgt->req_uctl_auth = 0; istgt->req_uctl_auth_mutual = 0; } else { ISTGT_ERRLOG("unknown auth\n"); return -1; } } } if (istgt->no_uctl_auth == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod None\n"); } else if (istgt->req_uctl_auth == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod Auto\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod %s %s\n", istgt->req_uctl_auth ? "CHAP" : "", istgt->req_uctl_auth_mutual ? "Mutual" : ""); } val = istgt_get_val(sp, "AuthGroup"); if (val == NULL) { istgt->uctl_auth_group = 0; } else { ag_tag = val; if (strcasecmp(ag_tag, "None") == 0) { ag_tag_i = 0; } else { if (strncasecmp(ag_tag, "AuthGroup", strlen("AuthGroup")) != 0 || sscanf(ag_tag, "%*[^0-9]%d", &ag_tag_i) != 1) { ISTGT_ERRLOG("auth group error\n"); return -1; } if (ag_tag_i == 0) { ISTGT_ERRLOG("invalid auth group %d\n", ag_tag_i); return -1; } } istgt->uctl_auth_group = ag_tag_i; } if (istgt->uctl_auth_group == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup None\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup AuthGroup%d\n", istgt->uctl_auth_group); } return 0; } istgt-20111008/src/istgt_sock.c000644 000000 000000 00000027466 11375523022 016324 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include "istgt.h" #include "istgt_sock.h" #include "istgt_misc.h" //#define USE_POLLWAIT #undef USE_POLLWAIT #define TIMEOUT_RW 60 #define POLLWAIT 1000 #define PORTNUMLEN 32 #ifndef AI_NUMERICSERV #define AI_NUMERICSERV 0 #endif int istgt_getaddr(int sock, char *saddr, int slen, char *caddr, int clen) { struct sockaddr_storage sa; socklen_t salen; int rc; memset(&sa, 0, sizeof sa); salen = sizeof sa; rc = getsockname(sock, (struct sockaddr *) &sa, &salen); if (rc != 0) { return -1; } rc = getnameinfo((struct sockaddr *) &sa, salen, saddr, slen, NULL, 0, NI_NUMERICHOST); if (rc != 0) { return -1; } memset(&sa, 0, sizeof sa); salen = sizeof sa; rc = getpeername(sock, (struct sockaddr *) &sa, &salen); if (rc != 0) { return -1; } rc = getnameinfo((struct sockaddr *) &sa, salen, caddr, clen, NULL, 0, NI_NUMERICHOST); if (rc != 0) { return -1; } return 0; } int istgt_listen(const char *ip, int port) { char buf[MAX_TMPBUF]; char portnum[PORTNUMLEN]; char *p; struct addrinfo hints, *res, *res0; int sock; int val = 1; int rc; if (ip == NULL) return -1; if (ip[0] == '[') { strlcpy(buf, ip + 1, sizeof buf); p = strchr(buf, ']'); if (p != NULL) *p = '\0'; ip = (const char *) &buf[0]; if (strcasecmp(ip, "*") == 0) { strlcpy(buf, "::", sizeof buf); ip = (const char *) &buf[0]; } } else { if (strcasecmp(ip, "*") == 0) { strlcpy(buf, "0.0.0.0", sizeof buf); ip = (const char *) &buf[0]; } } snprintf(portnum, sizeof portnum, "%d", port); memset(&hints, 0, sizeof hints); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; hints.ai_flags |= AI_PASSIVE; hints.ai_flags |= AI_NUMERICHOST; rc = getaddrinfo(ip, portnum, &hints, &res0); if (rc != 0) { return -1; } /* try listen */ sock = -1; for (res = res0; res != NULL; res = res->ai_next) { retry: sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) { /* error */ continue; } rc = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof val); if (rc != 0) { /* error */ continue; } rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val); if (rc != 0) { /* error */ continue; } rc = bind(sock, res->ai_addr, res->ai_addrlen); if (rc == -1 && errno == EINTR) { /* interrupted? */ close(sock); sock = -1; goto retry; } if (rc != 0) { /* try next family */ close(sock); sock = -1; continue; } /* bind OK */ rc = listen(sock, 2); if (rc != 0) { close(sock); sock = -1; break; } break; } freeaddrinfo(res0); if (sock < 0) { return -1; } return sock; } int istgt_connect(const char *host, int port) { char buf[MAX_TMPBUF]; char portnum[PORTNUMLEN]; char *p; struct addrinfo hints, *res, *res0; int sock; int val = 1; int rc; if (host == NULL) return -1; if (host[0] == '[') { strlcpy(buf, host + 1, sizeof buf); p = strchr(buf, ']'); if (p != NULL) *p = '\0'; host = (const char *) &buf[0]; if (strcasecmp(host, "*") == 0) { strlcpy(buf, "::", sizeof buf); host = (const char *) &buf[0]; } } else { if (strcasecmp(host, "*") == 0) { strlcpy(buf, "0.0.0.0", sizeof buf); host = (const char *) &buf[0]; } } snprintf(portnum, sizeof portnum, "%d", port); memset(&hints, 0, sizeof hints); hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_STREAM; hints.ai_flags = AI_NUMERICSERV; rc = getaddrinfo(host, portnum, &hints, &res0); if (rc != 0) { return -1; } /* try connect */ sock = -1; for (res = res0; res != NULL; res = res->ai_next) { retry: sock = socket(res->ai_family, res->ai_socktype, res->ai_protocol); if (sock < 0) { /* error */ continue; } rc = setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, &val, sizeof val); if (rc != 0) { /* error */ continue; } rc = connect(sock, res->ai_addr, res->ai_addrlen); if (rc == -1 && errno == EINTR) { /* interrupted? */ close(sock); sock = -1; goto retry; } if (rc != 0) { /* try next family */ close(sock); sock = -1; continue; } /* connect OK */ break; } freeaddrinfo(res0); if (sock < 0) { return -1; } return sock; } int istgt_set_recvtimeout(int s, int msec) { struct timeval tv; int rc; memset(&tv, 0, sizeof tv); tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; rc = setsockopt(s, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); if (rc != 0) return -1; return 0; } int istgt_set_sendtimeout(int s, int msec) { struct timeval tv; int rc; memset(&tv, 0, sizeof tv); tv.tv_sec = msec / 1000; tv.tv_usec = (msec % 1000) * 1000; rc = setsockopt(s, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof tv); if (rc != 0) return -1; return 0; } #ifdef USE_POLLWAIT static int can_read_socket(int s, int msec) { struct pollfd fds[1]; int rc; fds[0].fd = s; fds[0].events = POLLIN; retry: do { rc = poll(fds, 1, msec); } while (rc == -1 && errno == EINTR); if (rc == -1 && errno == EAGAIN) { goto retry; } if (rc < 0) { /* error */ return -1; } if (fds[0].revents & POLLIN) { /* read OK */ return 1; } return 0; } static int can_write_socket(int s, int msec) { struct pollfd fds[1]; int rc; fds[0].fd = s; fds[0].events = POLLOUT; retry: do { rc = poll(fds, 1, msec); } while (rc == -1 && errno == EINTR); if (rc == -1 && errno == EAGAIN) { goto retry; } if (rc < 0) { /* error */ return -1; } if (fds[0].revents & POLLOUT) { /* write OK */ return 1; } return 0; } #endif /* USE_POLLWAIT */ ssize_t istgt_read_socket(int s, void *buf, size_t nbytes, int timeout) { ssize_t n; #ifdef USE_POLLWAIT int msec = POLLWAIT; int rc; #endif /* USE_POLLWAIT */ if (nbytes == 0) return 0; #ifdef USE_POLLWAIT msec = timeout * 1000; rc = can_read_socket(s, msec); if (rc < 0) { return -1; } if (rc == 0) { /* TIMEOUT */ return -2; } retry: do { n = read(s, buf, nbytes); } while (n == -1 && errno == EINTR); if (n == -1 && errno == EAGAIN) { goto retry; } if (n < 0) { return -1; } #else do { n = recv(s, buf, nbytes, 0); } while (n == -1 && errno == EINTR); if (n == -1 && errno == EAGAIN) { /* TIMEOUT */ return -2; } if (n == -1) { return -1; } #endif /* USE_POLLWAIT */ return n; } ssize_t istgt_write_socket(int s, const void *buf, size_t nbytes, int timeout) { ssize_t n; #ifdef USE_POLLWAIT int msec = POLLWAIT; int rc; #endif /* USE_POLLWAIT */ if (nbytes == 0) return 0; #ifdef USE_POLLWAIT msec = timeout * 1000; rc = can_write_socket(s, msec); if (rc < 0) { return -1; } if (rc == 0) { /* TIMEOUT */ return -2; } retry: do { n = write(s, buf, nbytes); } while (n == -1 && errno == EINTR); if (n == -1 && errno == EAGAIN) { goto retry; } if (n < 0) { return -1; } #else do { n = send(s, buf, nbytes, 0); } while (n == -1 && (errno == EINTR || errno == EAGAIN)); if (n == -1) { return -1; } #endif /* USE_POLLWAIT */ return n; } ssize_t istgt_readline_socket(int sock, char *buf, size_t size, char *tmp, size_t tmpsize, int *tmpidx, int *tmpcnt, int timeout) { unsigned char *up, *utp; ssize_t maxsize; ssize_t total; ssize_t n; int got_cr; int idx, cnt; int ch; if (size < 2) { return -1; } up = (unsigned char *) buf; utp = (unsigned char *) tmp; maxsize = size - 2; /* LF + NUL */ total = 0; idx = *tmpidx; cnt = *tmpcnt; got_cr = 0; /* receive with LF */ while (total < maxsize) { /* fill temporary buffer */ if (idx == cnt) { *tmpidx = idx; up[total] = '\0'; n = istgt_read_socket(sock, tmp, tmpsize, timeout); if (n < 0) { if (total != 0) { up[total] = '\0'; return total; } return -1; } if (n == 0) { /* EOF */ up[total] = '\0'; return total; } /* got n bytes */ cnt = *tmpcnt = n; idx = 0; } /* copy from temporary until LF */ ch = utp[idx++]; if (got_cr && ch != '\n') { /* CR only */ /* back to temporary */ idx--; /* remove CR */ total--; break; } else if (ch == '\n') { if (got_cr) { /* CRLF */ /* remove CR */ total--; } else { /* LF only */ } break; } else if (ch == '\r') { got_cr = 1; } up[total++] = ch; } *tmpidx = idx; /* always append LF + NUL */ up[total++] = '\n'; up[total] = '\0'; return total; } static ssize_t istgt_allwrite_socket(int s, const void *buf, size_t nbytes, int timeout) { const uint8_t *cp; size_t total; ssize_t n; total = 0; cp = (const uint8_t *) buf; do { n = istgt_write_socket(s, cp + total, (nbytes - total), timeout); if (n < 0) { return n; } total += n; } while (total < nbytes); return total; } ssize_t istgt_writeline_socket(int sock, const char *buf, int timeout) { const unsigned char *up; ssize_t total; ssize_t n; int idx; int ch; up = (const unsigned char *) buf; total = 0; idx = 0; if (up[0] == '\0') { /* empty string */ n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } total = n; return total; } /* send with CRLF */ while ((ch = up[idx]) != '\0') { if (ch == '\r') { if (up[idx + 1] == '\n') { /* CRLF */ n = istgt_allwrite_socket(sock, up, idx + 2, timeout); if (n < 0) { return -1; } if (n != idx + 2) { return -1; } idx += 2; } else { /* CR Only */ n = istgt_allwrite_socket(sock, up, idx, timeout); if (n < 0) { return -1; } if (n != idx) { return -1; } idx += 1; n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } } } else if (ch == '\n') { /* LF Only */ n = istgt_allwrite_socket(sock, up, idx, timeout); if (n < 0) { return -1; } if (n != idx) { return -1; } idx += 1; n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } } else { idx++; continue; } up += idx; total += idx; idx = 0; } if (idx != 0) { /* no CRLF string */ n = istgt_allwrite_socket(sock, up, idx, timeout); if (n < 0) { return -1; } if (n != idx) { return -1; } n = istgt_allwrite_socket(sock, "\r\n", 2, timeout); if (n < 0) { return -1; } if (n != 2) { return -1; } up += idx; total += idx + 2; idx = 0; } return total; } istgt-20111008/src/istgt_iscsi_param.h000644 000000 000000 00000004450 11415046013 017643 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_ISCSI_PARAM_H #define ISTGT_ISCSI_PARAM_H #include typedef enum { ISPT_INVALID = -1, ISPT_NOTSPECIFIED = 0, ISPT_LIST, ISPT_NUMERICAL, ISPT_NUMERICAL_MAX, ISPT_DECLARATIVE, ISPT_BOOLEAN_OR, ISPT_BOOLEAN_AND, } ISCSI_PARAM_TYPE; typedef struct iscsi_param_t { struct iscsi_param_t *next; char *key; char *val; char *list; int type; } ISCSI_PARAM; void istgt_iscsi_param_free(ISCSI_PARAM *params); ISCSI_PARAM *istgt_iscsi_param_find(ISCSI_PARAM *params, const char *key); int istgt_iscsi_param_del(ISCSI_PARAM **params, const char *key); int istgt_iscsi_param_add(ISCSI_PARAM **params, const char *key, const char *val, const char *list, int type); int istgt_iscsi_param_set(ISCSI_PARAM *params, const char *key, const char *val); int istgt_iscsi_param_set_int(ISCSI_PARAM *params, const char *key, int val); int istgt_iscsi_parse_params(ISCSI_PARAM **params, const uint8_t *data, int len); #endif /* ISTGT_ISCSI_PARAM_H */ istgt-20111008/src/istgt_md5.c000644 000000 000000 00000004041 11317704406 016035 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include "istgt.h" #include "istgt_md5.h" int istgt_md5init(ISTGT_MD5CTX *md5ctx) { int rc; if (md5ctx == NULL) return -1; rc = MD5_Init(&md5ctx->md5ctx); return rc; } int istgt_md5final(void *md5, ISTGT_MD5CTX *md5ctx) { int rc; if (md5ctx == NULL || md5 == NULL) return -1; rc = MD5_Final(md5, &md5ctx->md5ctx); return rc; } int istgt_md5update(ISTGT_MD5CTX *md5ctx, const void *data, size_t len) { int rc; if (md5ctx == NULL) return -1; if (data == NULL || len <= 0) return 0; rc = MD5_Update(&md5ctx->md5ctx, data, len); return rc; } istgt-20111008/src/istgt_lu.c000644 000000 000000 00000210766 11643630071 016003 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_PTHREAD_NP_H #include #endif #include #include #include #include #include #include #include #include #ifdef HAVE_SYS_DISK_H #include #endif #ifdef HAVE_SYS_DISKLABEL_H #include #endif #ifdef __linux__ #include #endif #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_md5.h" #include "istgt_iscsi.h" #include "istgt_lu.h" #include "istgt_proto.h" #include "istgt_scsi.h" #define MAX_MASKBUF 128 static int istgt_lu_allow_ipv6(const char *netmask, const char *addr) { struct in6_addr in6_mask; struct in6_addr in6_addr; char mask[MAX_MASKBUF]; const char *p; int bits, bmask; int n; int i; if (netmask[0] != '[') return 0; p = strchr(netmask, ']'); if (p == NULL) return 0; n = p - (netmask + 1); if (n + 1 > sizeof mask) return 0; memcpy(mask, netmask + 1, n); mask[n] = '\0'; p++; if (p[0] == '/') { bits = (int) strtol(p + 1, NULL, 10); if (bits < 0 || bits > 128) return 0; } else { bits = 128; } #if 0 printf("input %s\n", addr); printf("mask %s / %d\n", mask, bits); #endif /* presentation to network order binary */ if (inet_pton(AF_INET6, mask, &in6_mask) <= 0 || inet_pton(AF_INET6, addr, &in6_addr) <= 0) { return 0; } /* check 128bits */ for (i = 0; i < (bits / 8); i++) { if (in6_mask.s6_addr[i] != in6_addr.s6_addr[i]) return 0; } if (bits % 8) { bmask = (0xffU << (8 - (bits % 8))) & 0xffU; if ((in6_mask.s6_addr[i] & bmask) != (in6_addr.s6_addr[i] & bmask)) return 0; } /* match */ return 1; } static int istgt_lu_allow_ipv4(const char *netmask, const char *addr) { struct in_addr in4_mask; struct in_addr in4_addr; char mask[MAX_MASKBUF]; const char *p; uint32_t bmask; int bits; int n; p = strchr(netmask, '/'); if (p == NULL) { p = netmask + strlen(netmask); } n = p - netmask; if (n + 1 > sizeof mask) return 0; memcpy(mask, netmask, n); mask[n] = '\0'; if (p[0] == '/') { bits = (int) strtol(p + 1, NULL, 10); if (bits < 0 || bits > 32) return 0; } else { bits = 32; } #if 0 printf("input %s\n", addr); printf("mask %s / %d\n", mask, bits); #endif /* presentation to network order binary */ if (inet_pton(AF_INET, mask, &in4_mask) <= 0 || inet_pton(AF_INET, addr, &in4_addr) <= 0) { return 0; } /* check 32bits */ bmask = (0xffffffffU << (32 - bits)) & 0xffffffffU; if ((ntohl(in4_mask.s_addr) & bmask) != (ntohl(in4_addr.s_addr) & bmask)) return 0; /* match */ return 1; } int istgt_lu_allow_netmask(const char *netmask, const char *addr) { if (netmask == NULL || addr == NULL) return 0; if (strcasecmp(netmask, "ALL") == 0) return 1; if (netmask[0] == '[') { /* IPv6 */ if (istgt_lu_allow_ipv6(netmask, addr)) return 1; } else { /* IPv4 */ if (istgt_lu_allow_ipv4(netmask, addr)) return 1; } return 0; } int istgt_lu_access(CONN_Ptr conn, ISTGT_LU_Ptr lu, const char *iqn, const char *addr) { ISTGT_Ptr istgt; INITIATOR_GROUP *igp; int pg_tag; int ig_tag; int rc; int i, j, k; if (conn == NULL || lu == NULL || iqn == NULL || addr == NULL) return 0; istgt = conn->istgt; pg_tag = conn->portal.tag; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "pg=%d, iqn=%s, addr=%s\n", pg_tag, iqn, addr); for (i = 0; i < lu->maxmap; i++) { /* skip excluding self portal group tag */ if (pg_tag != lu->map[i].pg_tag) continue; /* iqn is initiator group? */ ig_tag = lu->map[i].ig_tag; igp = istgt_lu_find_initiatorgroup(istgt, ig_tag); if (igp == NULL) { ISTGT_ERRLOG("LU%d: ig_tag not found\n", lu->num); continue; } for (j = 0; j < igp->ninitiators; j++) { /* deny initiators */ if (igp->initiators[j][0] == '!' && (strcasecmp(&igp->initiators[j][1], "ALL") == 0 || strcasecmp(&igp->initiators[j][1], iqn) == 0)) { /* NG */ ISTGT_WARNLOG("access denied from %s (%s) to %s (%s:%s,%d)\n", iqn, addr, conn->target_name, conn->portal.host, conn->portal.port, conn->portal.tag); return 0; } /* allow initiators */ if (strcasecmp(igp->initiators[j], "ALL") == 0 || strcasecmp(igp->initiators[j], iqn) == 0) { /* OK iqn, check netmask */ if (igp->nnetmasks == 0) { /* OK, empty netmask as ALL */ return 1; } for (k = 0; k < igp->nnetmasks; k++) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "netmask=%s, addr=%s\n", igp->netmasks[k], addr); rc = istgt_lu_allow_netmask(igp->netmasks[k], addr); if (rc > 0) { /* OK netmask */ return 1; } } /* NG netmask in this group */ } } } /* NG */ ISTGT_WARNLOG("access denied from %s (%s) to %s (%s:%s,%d)\n", iqn, addr, conn->target_name, conn->portal.host, conn->portal.port, conn->portal.tag); return 0; } int istgt_lu_visible(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu, const char *iqn, int pg_tag) { INITIATOR_GROUP *igp; int match_pg_tag; int ig_tag; int i, j; if (istgt == NULL || lu == NULL || iqn == NULL) return 0; /* pg_tag exist map? */ match_pg_tag = 0; for (i = 0; i < lu->maxmap; i++) { if (lu->map[i].pg_tag == pg_tag) { match_pg_tag = 1; break; } } if (match_pg_tag == 0) { /* cat't access from pg_tag */ return 0; } for (i = 0; i < lu->maxmap; i++) { /* iqn is initiator group? */ ig_tag = lu->map[i].ig_tag; igp = istgt_lu_find_initiatorgroup(istgt, ig_tag); if (igp == NULL) { ISTGT_ERRLOG("LU%d: ig_tag not found\n", lu->num); continue; } for (j = 0; j < igp->ninitiators; j++) { if (igp->initiators[j][0] == '!' && (strcasecmp(&igp->initiators[j][1], "ALL") == 0 || strcasecmp(&igp->initiators[j][1], iqn) == 0)) { /* NG */ return 0; } if (strcasecmp(igp->initiators[j], "ALL") == 0 || strcasecmp(igp->initiators[j], iqn) == 0) { /* OK iqn, no check addr */ return 1; } } } /* NG */ return 0; } int istgt_lu_sendtargets(CONN_Ptr conn, const char *iiqn, const char *iaddr, const char *tiqn, uint8_t *data, int alloc_len, int data_len) { char buf[MAX_TMPBUF]; ISTGT_Ptr istgt; ISTGT_LU_Ptr lu; char *host; int total; int len; int rc; int pg_tag; int i, j, k; if (conn == NULL) return 0; istgt = conn->istgt; total = data_len; if (alloc_len < 1) { return 0; } if (total > alloc_len) { total = alloc_len; data[total - 1] = '\0'; return total; } if (alloc_len - total < 1) { ISTGT_ERRLOG("data space small %d\n", alloc_len); return total; } MTX_LOCK(&istgt->mutex); for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = istgt->logical_unit[i]; if (lu == NULL) continue; if (strcasecmp(tiqn, "ALL") != 0 && strcasecmp(tiqn, lu->name) != 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SKIP iqn=%s for %s from %s (%s)\n", tiqn, lu->name, iiqn, iaddr); continue; } rc = istgt_lu_visible(istgt, lu, iiqn, conn->portal.tag); if (rc == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SKIP iqn=%s for %s from %s (%s)\n", tiqn, lu->name, iiqn, iaddr); continue; } /* DO SENDTARGETS */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "OK iqn=%s for %s from %s (%s)\n", tiqn, lu->name, iiqn, iaddr); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "TargetName=%s\n", lu->name); len = snprintf((char *) data + total, alloc_len - total, "TargetName=%s", lu->name); total += len + 1; for (j = 0; j < lu->maxmap; j++) { pg_tag = lu->map[j].pg_tag; /* skip same pg_tag */ for (k = 0; k < j; k++) { if (lu->map[k].pg_tag == pg_tag) { goto skip_pg_tag; } } /* write to data */ for (k = 0; k < istgt->nportal; k++) { if (istgt->portal[k].tag == pg_tag) { if (alloc_len - total < 1) { MTX_UNLOCK(&istgt->mutex); ISTGT_ERRLOG("data space small %d\n", alloc_len); return total; } host = istgt->portal[k].host; /* wildcard? */ if (strcasecmp(host, "[::]") == 0 || strcasecmp(host, "[*]") == 0 || strcasecmp(host, "0.0.0.0") == 0 || strcasecmp(host, "*") == 0) { if ((strcasecmp(host, "[::]") == 0 || strcasecmp(host, "[*]") == 0) && conn->initiator_family == AF_INET6) { snprintf(buf, sizeof buf, "[%s]", conn->target_addr); host = buf; } else if ((strcasecmp(host, "0.0.0.0") == 0 || strcasecmp(host, "*") == 0) && conn->initiator_family == AF_INET) { snprintf(buf, sizeof buf, "%s", conn->target_addr); host = buf; } else { /* skip portal for the family */ continue; } } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "TargetAddress=%s:%s,%d\n", host, istgt->portal[k].port, istgt->portal[k].tag); len = snprintf((char *) data + total, alloc_len - total, "TargetAddress=%s:%s,%d", host, istgt->portal[k].port, istgt->portal[k].tag); total += len + 1; } } skip_pg_tag: ; } } MTX_UNLOCK(&istgt->mutex); return total; } ISTGT_LU_Ptr istgt_lu_find_target(ISTGT_Ptr istgt, const char *target_name) { ISTGT_LU_Ptr lu; int i; if (istgt == NULL || target_name == NULL) return NULL; for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = istgt->logical_unit[i]; if (lu == NULL) continue; if (strcasecmp(target_name, lu->name) == 0) { return lu; } } ISTGT_WARNLOG("can't find target %s\n", target_name); return NULL; } uint16_t istgt_lu_allocate_tsih(ISTGT_LU_Ptr lu, const char *initiator_port, int tag) { uint16_t tsih; int retry = 10; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_allocate_tsih\n"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "initiator_port=%s, tag=%d\n", initiator_port, tag); if (lu == NULL || initiator_port == NULL || tag == 0) return 0; /* tsih 0 is reserved */ tsih = 0; MTX_LOCK(&lu->mutex); #if 0 for (i = 1; i < MAX_LU_TSIH; i++) { if (lu->tsih[i].initiator_port == NULL) continue; if (tag != lu->tsih[i].tag) continue; if (strcasecmp(initiator_port, lu->tsih[i].initiator_port) == 0) { tsih = lu->tsih[i].tsih; break; } } #endif if (tsih == 0) { if (lu->maxtsih >= MAX_LU_TSIH) { ISTGT_ERRLOG("LU%d: tsih is maximum\n", lu->num); MTX_UNLOCK(&lu->mutex); return 0; } retry: lu->last_tsih++; tsih = lu->last_tsih; if (tsih == 0) { if (retry > 0) { retry--; goto retry; } ISTGT_ERRLOG("LU%d: retry error\n", lu->num); MTX_UNLOCK(&lu->mutex); return 0; } for (i = 1; i < MAX_LU_TSIH; i++) { if (lu->tsih[i].initiator_port != NULL && lu->tsih[i].tsih == tsih) { ISTGT_ERRLOG("tsih is found in list\n"); if (retry > 0) { retry--; goto retry; } ISTGT_ERRLOG("LU%d: retry error\n", lu->num); MTX_UNLOCK(&lu->mutex); return 0; } } for (i = 1; i < MAX_LU_TSIH; i++) { if (lu->tsih[i].initiator_port == NULL) { lu->tsih[i].tag = tag; lu->tsih[i].tsih = tsih; lu->tsih[i].initiator_port = xstrdup(initiator_port); lu->maxtsih++; break; } } } MTX_UNLOCK(&lu->mutex); return tsih; } void istgt_lu_free_tsih(ISTGT_LU_Ptr lu, uint16_t tsih, char *initiator_port) { int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_free_tsih\n"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "tsih=%u, initiator_port=%s\n", tsih, initiator_port); if (lu == NULL || initiator_port == NULL) return; if (tsih == 0) return; MTX_LOCK(&lu->mutex); for (i = 1; i < MAX_LU_TSIH; i++) { if (lu->tsih[i].initiator_port == NULL) continue; if (lu->tsih[i].tsih != tsih) continue; if (strcasecmp(initiator_port, lu->tsih[i].initiator_port) == 0) { lu->tsih[i].tag = 0; lu->tsih[i].tsih = 0; xfree(lu->tsih[i].initiator_port); lu->tsih[i].initiator_port = NULL; lu->maxtsih--; break; } } MTX_UNLOCK(&lu->mutex); return; } char * istgt_lu_get_media_flags_string(int flags, char *buf, size_t len) { char *p; size_t rest; p = buf; rest = len; if (flags & ISTGT_LU_FLAG_MEDIA_READONLY) { snprintf(p, rest, "%s", "ro"); } else { snprintf(p, rest, "%s", "rw"); } p = buf + strlen(buf); rest = len - strlen(buf); if (flags & ISTGT_LU_FLAG_MEDIA_EXTEND) { snprintf(p, rest, ",%s", "extend"); } p = buf + strlen(buf); rest = len - strlen(buf); if (flags & ISTGT_LU_FLAG_MEDIA_DYNAMIC) { snprintf(p, rest, ",%s", "dynamic"); } return buf; } uint64_t istgt_lu_get_devsize(const char *file) { uint64_t val; struct stat st; int fd; int rc; val = 0ULL; rc = lstat(file, &st); if (rc != 0) return val; if (!S_ISCHR(st.st_mode) && !S_ISBLK(st.st_mode)) return val; fd = open(file, O_RDONLY, 0); if (fd >= 0) { #ifdef DIOCGMEDIASIZE if (val == 0) { off_t offset; rc = ioctl(fd, DIOCGMEDIASIZE, &offset); if (rc != -1) { val = (uint64_t) offset; } } #endif /* DIOCGMEDIASIZE */ #ifdef DIOCGDINFO if (val == 0) { struct disklabel dl; rc = ioctl(fd, DIOCGDINFO, &dl); if (rc != -1) { val = (uint64_t) dl.d_secperunit; val *= (uint64_t) dl.d_secsize; } } #endif /* DIOCGDINFO */ #if defined(DKIOCGETBLOCKSIZE) && defined(DKIOCGETBLOCKCOUNT) if (val == 0) { uint32_t blocklen; uint64_t blockcnt; rc = ioctl(fd, DKIOCGETBLOCKSIZE, &blocklen); if (rc != -1) { rc = ioctl(fd, DKIOCGETBLOCKCOUNT, &blockcnt); if (rc != -1) { val = (uint64_t) blocklen; val *= (uint64_t) blockcnt; } } } #endif /* DKIOCGETBLOCKSIZE && DKIOCGETBLOCKCOUNT */ #ifdef __linux__ #ifdef BLKGETSIZE64 if (val == 0) { uint64_t blocksize; rc = ioctl(fd, BLKGETSIZE64, &blocksize); if (rc != -1) { val = (uint64_t) blocksize; } } #endif /* BLKGETSIZE64 */ #ifdef BLKGETSIZE if (val == 0) { uint32_t blocksize; rc = ioctl(fd, BLKGETSIZE, &blocksize); if (rc != -1) { val = (uint64_t) 512; val *= (uint64_t) blocksize; } } #endif /* BLKGETSIZE */ #endif /* __linux__ */ if (val == 0) { ISTGT_ERRLOG("unknown device size\n"); } (void) close(fd); } else { if (g_trace_flag) { ISTGT_WARNLOG("open error %s (errno=%d)\n", file, errno); } val = 0ULL; } return val; } uint64_t istgt_lu_get_filesize(const char *file) { uint64_t val; struct stat st; int rc; val = 0ULL; rc = lstat(file, &st); if (rc < 0) return val; if (S_ISLNK(st.st_mode)) return val; if (S_ISCHR(st.st_mode)) { val = istgt_lu_get_devsize(file); } else if (S_ISBLK(st.st_mode)) { val = istgt_lu_get_devsize(file); } else if (S_ISREG(st.st_mode)) { val = st.st_size; } else { ISTGT_ERRLOG("lstat is neither REG, CHR nor BLK\n"); val = 0ULL; } return val; } uint64_t istgt_lu_parse_size(const char *size) { uint64_t val, val1, val2; char *endp, *p; size_t idx; int sign; val1 = (uint64_t) strtoull(size, &endp, 10); val = val1; val2 = 0; if (endp != NULL) { p = endp; switch (toupper((int) *p)) { case 'Z': val1 *= (uint64_t) 1024ULL; case 'E': val1 *= (uint64_t) 1024ULL; case 'P': val1 *= (uint64_t) 1024ULL; case 'T': val1 *= (uint64_t) 1024ULL; case 'G': val1 *= (uint64_t) 1024ULL; case 'M': val1 *= (uint64_t) 1024ULL; case 'K': val1 *= (uint64_t) 1024ULL; break; } val = val1; p++; idx = strspn(p, "Bb \t"); p += idx; if (*p == '-' || *p == '+') { sign = (int) *p++; idx = strspn(p, " \t"); p += idx; val2 = (uint64_t) strtoull(p, &endp, 10); if (endp != NULL) { p = endp; switch (toupper((int) *p)) { case 'Z': val2 *= (uint64_t) 1024ULL; case 'E': val2 *= (uint64_t) 1024ULL; case 'P': val2 *= (uint64_t) 1024ULL; case 'T': val2 *= (uint64_t) 1024ULL; case 'G': val2 *= (uint64_t) 1024ULL; case 'M': val2 *= (uint64_t) 1024ULL; case 'K': val2 *= (uint64_t) 1024ULL; break; } } if (sign == '-') { if (val2 > val1) { /* underflow */ val = (uint64_t) 0ULL; } else { val = val1 - val2; } } else { if (val2 > (UINT64_MAX - val1)) { /* overflow */ val = UINT64_MAX; } else { val = val1 + val2; } } } } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "size=%s, val=%"PRIu64", val1=%"PRIu64", val2=%"PRIu64"\n", size, val, val1, val2); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "size=%s, val=%"PRIx64", val1=%"PRIx64", val2=%"PRIx64"\n", size, val, val1, val2); return val; } int istgt_lu_parse_media_flags(const char *flags) { char buf[MAX_TMPBUF]; const char *delim = ","; char *next_p; char *p; int mflags; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "mflags=%s\n", flags); mflags = 0; strlcpy(buf, flags, MAX_TMPBUF); next_p = buf; while ((p = strsep(&next_p, delim)) != NULL) { if (strcasecmp(p, "ro") == 0) { mflags |= ISTGT_LU_FLAG_MEDIA_READONLY; } else if (strcasecmp(p, "rw") == 0) { mflags &= ~ISTGT_LU_FLAG_MEDIA_READONLY; } else if (strcasecmp(p, "extend") == 0) { mflags |= ISTGT_LU_FLAG_MEDIA_EXTEND; } else if (strcasecmp(p, "dynamic") == 0) { mflags |= ISTGT_LU_FLAG_MEDIA_DYNAMIC; } else { ISTGT_ERRLOG("unknown media flag %.64s\n", p); } } return mflags; } uint64_t istgt_lu_parse_media_size(const char *file, const char *size, int *flags) { uint64_t msize, fsize; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "msize=%s\n", size); if (strcasecmp(file, "/dev/null") == 0) { return 0; } if (strcasecmp(size, "Auto") == 0 || strcasecmp(size, "Size") == 0) { msize = istgt_lu_get_filesize(file); if (msize == 0) { msize = ISTGT_LU_MEDIA_SIZE_MIN; } *flags |= ISTGT_LU_FLAG_MEDIA_AUTOSIZE; } else { msize = istgt_lu_parse_size(size); if (*flags & ISTGT_LU_FLAG_MEDIA_EXTEND) { fsize = istgt_lu_get_filesize(file); if (fsize > msize) { msize = fsize; } } } if (*flags & ISTGT_LU_FLAG_MEDIA_DYNAMIC) { if (msize < ISTGT_LU_MEDIA_SIZE_MIN) { msize = ISTGT_LU_MEDIA_SIZE_MIN; } } else { if (msize < ISTGT_LU_MEDIA_SIZE_MIN) { ISTGT_ERRLOG("media size too small\n"); return 0ULL; } } return msize; } PORTAL * istgt_lu_find_portalgroup(ISTGT_Ptr istgt, int tag) { PORTAL *pp; int i; for (i = 0; i < istgt->nportal; i++) { if (istgt->portal[i].tag == tag) { pp = &istgt->portal[i]; return pp; } } return NULL; } INITIATOR_GROUP * istgt_lu_find_initiatorgroup(ISTGT_Ptr istgt, int tag) { INITIATOR_GROUP *igp; int i; for (i = 0; i < istgt->ninitiator_group; i++) { if (istgt->initiator_group[i].tag == tag) { igp = &istgt->initiator_group[i]; return igp; } } return NULL; } static int istgt_lu_check_iscsi_name(const char *name) { const unsigned char *up = (const unsigned char *) name; size_t n; /* valid iSCSI name? */ for (n = 0; up[n] != 0; n++) { if (up[n] > 0x00U && up[n] <= 0x2cU) return -1; if (up[n] == 0x2fU) return -1; if (up[n] >= 0x3bU && up[n] <= 0x40U) return -1; if (up[n] >= 0x5bU && up[n] <= 0x60U) return -1; if (up[n] >= 0x7bU && up[n] <= 0x7fU) return -1; if (isspace(up[n])) return -1; } /* valid format? */ if (strncasecmp(name, "iqn.", 4) == 0) { /* iqn.YYYY-MM.reversed.domain.name */ if (!isdigit(up[4]) || !isdigit(up[5]) || !isdigit(up[6]) || !isdigit(up[7]) || up[8] != '-' || !isdigit(up[9]) || !isdigit(up[10]) || up[11] != '.') { ISTGT_ERRLOG("invalid iqn format. " "expect \"iqn.YYYY-MM.reversed.domain.name\"\n"); return -1; } } else if (strncasecmp(name, "eui.", 4) == 0) { /* EUI-64 -> 16bytes */ /* XXX */ } else if (strncasecmp(name, "naa.", 4) == 0) { /* 64bit -> 16bytes, 128bit -> 32bytes */ /* XXX */ } /* OK */ return 0; } static uint64_t istgt_lu_get_nbserial(const char *nodebase) { ISTGT_MD5CTX md5ctx; uint8_t nbsmd5[ISTGT_MD5DIGEST_LEN]; char buf[MAX_TMPBUF]; uint64_t nbs; int idx; int i; snprintf(buf, sizeof buf, "%s", nodebase); if (strcasecmp(buf, "iqn.2007-09.jp.ne.peach.istgt") == 0 || strcasecmp(buf, "iqn.2007-09.jp.ne.peach") == 0) { /* always zero */ return 0; } istgt_md5init(&md5ctx); istgt_md5update(&md5ctx, buf, strlen(buf)); istgt_md5final(nbsmd5, &md5ctx); nbs = 0U; idx = ISTGT_MD5DIGEST_LEN - 8; if (idx < 0) { ISTGT_WARNLOG("missing MD5 length\n"); idx = 0; } for (i = idx; i < ISTGT_MD5DIGEST_LEN; i++) { nbs |= (uint64_t) nbsmd5[i]; nbs = nbs << 8; } return nbs; } static int istgt_lu_set_local_settings(ISTGT_Ptr istgt, CF_SECTION *sp, ISTGT_LU_Ptr lu) { const char *val; val = istgt_get_val(sp, "MaxOutstandingR2T"); if (val == NULL) { lu->MaxOutstandingR2T = lu->istgt->MaxOutstandingR2T; } else { lu->MaxOutstandingR2T = (int)strtol(val, NULL, 10); if (lu->MaxOutstandingR2T < 1) { lu->MaxOutstandingR2T = DEFAULT_MAXOUTSTANDINGR2T; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxOutstandingR2T %d\n", lu->MaxOutstandingR2T); } val = istgt_get_val(sp, "DefaultTime2Wait"); if (val == NULL) { lu->DefaultTime2Wait = lu->istgt->DefaultTime2Wait; } else { lu->DefaultTime2Wait = (int)strtol(val, NULL, 10); if (lu->DefaultTime2Wait < 0) { lu->DefaultTime2Wait = DEFAULT_DEFAULTTIME2WAIT; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DefaultTime2Wait %d\n", lu->DefaultTime2Wait); } val = istgt_get_val(sp, "DefaultTime2Retain"); if (val == NULL) { lu->DefaultTime2Retain = lu->istgt->DefaultTime2Retain; } else { lu->DefaultTime2Retain = (int)strtol(val, NULL, 10); if (lu->DefaultTime2Retain < 0) { lu->DefaultTime2Retain = DEFAULT_DEFAULTTIME2RETAIN; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DefaultTime2Retain %d\n", lu->DefaultTime2Retain); } /* check size limit - RFC3720(12.15, 12.16, 12.17) */ if (lu->MaxOutstandingR2T > 65535) { ISTGT_ERRLOG("MaxOutstandingR2T(%d) > 65535\n", lu->MaxOutstandingR2T); return -1; } if (lu->DefaultTime2Wait > 3600) { ISTGT_ERRLOG("DefaultTime2Wait(%d) > 3600\n", lu->DefaultTime2Wait); return -1; } if (lu->DefaultTime2Retain > 3600) { ISTGT_ERRLOG("DefaultTime2Retain(%d) > 3600\n", lu->DefaultTime2Retain); return -1; } val = istgt_get_val(sp, "FirstBurstLength"); if (val == NULL) { lu->FirstBurstLength = lu->istgt->FirstBurstLength; } else { lu->FirstBurstLength = (int)strtol(val, NULL, 10); if (lu->FirstBurstLength < 0) { lu->FirstBurstLength = DEFAULT_FIRSTBURSTLENGTH; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "FirstBurstLength %d\n", lu->FirstBurstLength); } val = istgt_get_val(sp, "MaxBurstLength"); if (val == NULL) { lu->MaxBurstLength = lu->istgt->MaxBurstLength; } else { lu->MaxBurstLength = (int)strtol(val, NULL, 10); if (lu->MaxBurstLength < 0) { lu->MaxBurstLength = DEFAULT_MAXBURSTLENGTH; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxBurstLength %d\n", lu->MaxBurstLength); } val = istgt_get_val(sp, "MaxRecvDataSegmentLength"); if (val == NULL) { lu->MaxRecvDataSegmentLength = lu->istgt->MaxRecvDataSegmentLength; } else { lu->MaxRecvDataSegmentLength = (int)strtol(val, NULL, 10); if (lu->MaxRecvDataSegmentLength < 0) { lu->MaxRecvDataSegmentLength = DEFAULT_MAXRECVDATASEGMENTLENGTH; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxRecvDataSegmentLength %d\n", lu->MaxRecvDataSegmentLength); } /* check size limit (up to 24bits - RFC3720(12.12)) */ if (lu->MaxBurstLength < 512) { ISTGT_ERRLOG("MaxBurstLength(%d) < 512\n", lu->MaxBurstLength); return -1; } if (lu->FirstBurstLength < 512) { ISTGT_ERRLOG("FirstBurstLength(%d) < 512\n", lu->FirstBurstLength); return -1; } if (lu->FirstBurstLength > lu->MaxBurstLength) { ISTGT_ERRLOG("FirstBurstLength(%d) > MaxBurstLength(%d)\n", lu->FirstBurstLength, istgt->MaxBurstLength); return -1; } if (lu->MaxBurstLength > 0x00ffffff) { ISTGT_ERRLOG("MaxBurstLength(%d) > 0x00ffffff\n", lu->MaxBurstLength); return -1; } if (lu->MaxRecvDataSegmentLength < 512) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) < 512\n", lu->MaxRecvDataSegmentLength); return -1; } if (lu->MaxRecvDataSegmentLength > 0x00ffffff) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) > 0x00ffffff\n", lu->MaxRecvDataSegmentLength); return -1; } val = istgt_get_val(sp, "InitialR2T"); if (val == NULL) { lu->InitialR2T = lu->istgt->InitialR2T; } else { if (strcasecmp(val, "Yes") == 0) { lu->InitialR2T = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 lu->InitialR2T = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "InitialR2T %s\n", lu->InitialR2T ? "Yes" : "No"); } val = istgt_get_val(sp, "ImmediateData"); if (val == NULL) { lu->ImmediateData = lu->istgt->ImmediateData; } else { if (strcasecmp(val, "Yes") == 0) { lu->ImmediateData = 1; } else if (strcasecmp(val, "No") == 0) { lu->ImmediateData = 0; } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ImmediateData %s\n", lu->ImmediateData ? "Yes" : "No"); } val = istgt_get_val(sp, "DataPDUInOrder"); if (val == NULL) { lu->DataPDUInOrder = lu->istgt->DataPDUInOrder; } else { if (strcasecmp(val, "Yes") == 0) { lu->DataPDUInOrder = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 lu->DataPDUInOrder = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DataPDUInOrder %s\n", lu->DataPDUInOrder ? "Yes" : "No"); } val = istgt_get_val(sp, "DataSequenceInOrder"); if (val == NULL) { lu->DataSequenceInOrder = lu->istgt->DataSequenceInOrder; } else { if (strcasecmp(val, "Yes") == 0) { lu->DataSequenceInOrder = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 lu->DataSequenceInOrder = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DataSequenceInOrder %s\n", lu->DataSequenceInOrder ? "Yes" : "No"); } val = istgt_get_val(sp, "ErrorRecoveryLevel"); if (val == NULL) { lu->ErrorRecoveryLevel = lu->istgt->ErrorRecoveryLevel; } else { lu->ErrorRecoveryLevel = (int)strtol(val, NULL, 10); if (lu->ErrorRecoveryLevel < 0) { lu->ErrorRecoveryLevel = DEFAULT_ERRORRECOVERYLEVEL; } else if (lu->ErrorRecoveryLevel == 0) { lu->ErrorRecoveryLevel = 0; } else if (lu->ErrorRecoveryLevel == 1) { #if 0 lu->ErrorRecoveryLevel = 1; #else ISTGT_ERRLOG("not supported value %d\n", lu->ErrorRecoveryLevel); return -1; #endif } else if (lu->ErrorRecoveryLevel == 2) { #if 0 lu->ErrorRecoveryLevel = 2; #else ISTGT_ERRLOG("not supported value %d\n", lu->ErrorRecoveryLevel); return -1; #endif } else { ISTGT_ERRLOG("not supported value %d\n", lu->ErrorRecoveryLevel); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ErrorRecoveryLevel %d\n", istgt->ErrorRecoveryLevel); } return 0; } static int istgt_lu_add_unit(ISTGT_Ptr istgt, CF_SECTION *sp) { char buf[MAX_TMPBUF], buf2[MAX_TMPBUF]; ISTGT_LU_Ptr lu; const char *vendor, *product, *revision, *serial; const char *pg_tag, *ig_tag; const char *ag_tag; const char *flags, *file, *size; const char *key, *val; uint64_t msize; uint64_t nbs64; int pg_tag_i, ig_tag_i; int ag_tag_i; int rpm, formfactor; int mflags; int slot; int nbs; int i, j, k; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "add unit %d\n", sp->num); if (sp->num >= MAX_LOGICAL_UNIT) { ISTGT_ERRLOG("LU%d: over maximum unit number\n", sp->num); return -1; } if (istgt->logical_unit[sp->num] != NULL) { ISTGT_ERRLOG("LU%d: duplicate unit\n", sp->num); return -1; } lu = xmalloc(sizeof *lu); memset(lu, 0, sizeof *lu); lu->num = sp->num; lu->istgt = istgt; istgt_lu_set_state(lu, ISTGT_STATE_INVALID); nbs64 = istgt_lu_get_nbserial(istgt->nodebase); #if 0 /* disabled now */ nbs = (int) (nbs64 % 900) * 100000; #else nbs = 0; #endif val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } val = istgt_get_val(sp, "TargetName"); if (val == NULL) { ISTGT_ERRLOG("LU%d: TargetName not found\n", lu->num); goto error_return; } if (strncasecmp(val, "iqn.", 4) != 0 && strncasecmp(val, "eui.", 4) != 0 && strncasecmp(val, "naa.", 4) != 0) { snprintf(buf, sizeof buf, "%s:%s", istgt->nodebase, val); } else { snprintf(buf, sizeof buf, "%s", val); } if (istgt_lu_check_iscsi_name(buf) != 0) { ISTGT_ERRLOG("TargetName %s contains an invalid character or format.\n", buf); #if 0 goto error_return; #endif } lu->name = xstrdup(buf); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "TargetName %s\n", lu->name); val = istgt_get_val(sp, "TargetAlias"); if (val == NULL) { lu->alias = NULL; } else { lu->alias = xstrdup(val); } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "TargetAlias %s\n", lu->alias); val = istgt_get_val(sp, "Mapping"); if (val == NULL) { /* no map */ lu->maxmap = 0; } else { lu->maxmap = 0; for (i = 0; ; i++) { val = istgt_get_nmval(sp, "Mapping", i, 0); if (val == NULL) break; if (lu->maxmap >= MAX_LU_MAP) { ISTGT_ERRLOG("LU%d: too many mapping\n", lu->num); goto error_return; } pg_tag = istgt_get_nmval(sp, "Mapping", i, 0); ig_tag = istgt_get_nmval(sp, "Mapping", i, 1); if (pg_tag == NULL || ig_tag == NULL) { ISTGT_ERRLOG("LU%d: mapping error\n", lu->num); goto error_return; } if (strncasecmp(pg_tag, "PortalGroup", strlen("PortalGroup")) != 0 || sscanf(pg_tag, "%*[^0-9]%d", &pg_tag_i) != 1) { ISTGT_ERRLOG("LU%d: mapping portal error\n", lu->num); goto error_return; } if (strncasecmp(ig_tag, "InitiatorGroup", strlen("InitiatorGroup")) != 0 || sscanf(ig_tag, "%*[^0-9]%d", &ig_tag_i) != 1) { ISTGT_ERRLOG("LU%d: mapping initiator error\n", lu->num); goto error_return; } if (pg_tag_i < 1 || ig_tag_i < 1) { ISTGT_ERRLOG("LU%d: invalid group tag\n", lu->num); goto error_return; } if (istgt_lu_find_portalgroup(istgt, pg_tag_i) == NULL) { ISTGT_ERRLOG("LU%d: PortalGroup%d not found\n", lu->num, pg_tag_i); goto error_return; } if (istgt_lu_find_initiatorgroup(istgt, ig_tag_i) == NULL) { ISTGT_ERRLOG("LU%d: InitiatorGroup%d not found\n", lu->num, ig_tag_i); goto error_return; } lu->map[i].pg_tag = pg_tag_i; lu->map[i].pg_aas = AAS_ACTIVE_OPTIMIZED; //lu->map[i].pg_aas = AAS_ACTIVE_NON_OPTIMIZED; lu->map[i].pg_aas |= AAS_STATUS_IMPLICIT; lu->map[i].ig_tag = ig_tag_i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Mapping PortalGroup%d InitiatorGroup%d\n", lu->map[i].pg_tag, lu->map[i].ig_tag); lu->maxmap = i + 1; } } if (lu->maxmap == 0) { ISTGT_ERRLOG("LU%d: no Mapping\n", lu->num); goto error_return; } val = istgt_get_val(sp, "AuthMethod"); if (val == NULL) { /* none */ lu->no_auth_chap = 0; lu->auth_chap = 0; lu->auth_chap_mutual = 0; } else { lu->no_auth_chap = 0; for (i = 0; ; i++) { val = istgt_get_nmval(sp, "AuthMethod", 0, i); if (val == NULL) break; if (strcasecmp(val, "CHAP") == 0) { lu->auth_chap = 1; } else if (strcasecmp(val, "Mutual") == 0) { lu->auth_chap_mutual = 1; } else if (strcasecmp(val, "Auto") == 0) { lu->auth_chap = 0; lu->auth_chap_mutual = 0; } else if (strcasecmp(val, "None") == 0) { lu->no_auth_chap = 1; lu->auth_chap = 0; lu->auth_chap_mutual = 0; } else { ISTGT_ERRLOG("LU%d: unknown auth\n", lu->num); goto error_return; } } if (lu->auth_chap_mutual && !lu->auth_chap) { ISTGT_ERRLOG("LU%d: Mutual but not CHAP\n", lu->num); goto error_return; } } if (lu->no_auth_chap != 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod None\n"); } else if (lu->auth_chap == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod Auto\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthMethod %s %s\n", lu->auth_chap ? "CHAP" : "", lu->auth_chap_mutual ? "Mutual" : ""); } val = istgt_get_val(sp, "AuthGroup"); if (val == NULL) { lu->auth_group = 0; } else { ag_tag = val; if (strcasecmp(ag_tag, "None") == 0) { ag_tag_i = 0; } else { if (strncasecmp(ag_tag, "AuthGroup", strlen("AuthGroup")) != 0 || sscanf(ag_tag, "%*[^0-9]%d", &ag_tag_i) != 1) { ISTGT_ERRLOG("LU%d: auth group error\n", lu->num); goto error_return; } if (ag_tag_i == 0) { ISTGT_ERRLOG("LU%d: invalid auth group %d\n", ag_tag_i); goto error_return; } } lu->auth_group = ag_tag_i; } if (lu->auth_group == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup None\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthGroup AuthGroup%d\n", lu->auth_group); } val = istgt_get_val(sp, "UseDigest"); if (val != NULL) { for (i = 0; ; i++) { val = istgt_get_nmval(sp, "UseDigest", 0, i); if (val == NULL) break; if (strcasecmp(val, "Header") == 0) { lu->header_digest = 1; } else if (strcasecmp(val, "Data") == 0) { lu->data_digest = 1; } else if (strcasecmp(val, "Auto") == 0) { lu->header_digest = 0; lu->data_digest = 0; } else { ISTGT_ERRLOG("LU%d: unknown digest\n", lu->num); goto error_return; } } } if (lu->header_digest == 0 && lu->data_digest == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "UseDigest Auto\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "UseDigest %s %s\n", lu->header_digest ? "Header" : "", lu->data_digest ? "Data" : ""); } val = istgt_get_val(sp, "ReadOnly"); if (val == NULL) { lu->readonly = 0; } else if (strcasecmp(val, "Yes") == 0) { lu->readonly = 1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ReadOnly %s\n", lu->readonly ? "Yes" : "No"); val = istgt_get_val(sp, "UnitType"); if (val == NULL) { ISTGT_ERRLOG("LU%d: unknown unit type\n", lu->num); goto error_return; } if (strcasecmp(val, "Pass") == 0) { lu->type = ISTGT_LU_TYPE_PASS; } else if (strcasecmp(val, "Disk") == 0) { lu->type = ISTGT_LU_TYPE_DISK; } else if (strcasecmp(val, "DVD") == 0) { lu->type = ISTGT_LU_TYPE_DVD; } else if (strcasecmp(val, "Tape") == 0) { lu->type = ISTGT_LU_TYPE_TAPE; } else { ISTGT_ERRLOG("LU%d: unknown unit type\n", lu->num); goto error_return; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "UnitType %d (%s)\n", lu->type, val); val = istgt_get_val(sp, "UnitOnline"); if (val == NULL) { lu->online = 1; } else if (strcasecmp(val, "Yes") == 0) { lu->online = 1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "UnitOnline %s\n", lu->online ? "Yes" : "No"); vendor = istgt_get_nmval(sp, "UnitInquiry", 0, 0); product = istgt_get_nmval(sp, "UnitInquiry", 0, 1); revision = istgt_get_nmval(sp, "UnitInquiry", 0, 2); serial = istgt_get_nmval(sp, "UnitInquiry", 0, 3); switch (lu->type) { case ISTGT_LU_TYPE_DISK: if (vendor == NULL || strlen(vendor) == 0) vendor = DEFAULT_LU_VENDOR_DISK; if (product == NULL || strlen(product) == 0) product = DEFAULT_LU_PRODUCT_DISK; if (revision == NULL || strlen(revision) == 0) revision = DEFAULT_LU_REVISION_DISK; if (serial == NULL || strlen(serial) == 0) { snprintf(buf, sizeof buf, "%.8d", 10000000 + nbs + lu->num); serial = (const char *) &buf[0]; } break; case ISTGT_LU_TYPE_DVD: if (vendor == NULL || strlen(vendor) == 0) vendor = DEFAULT_LU_VENDOR_DVD; if (product == NULL || strlen(product) == 0) product = DEFAULT_LU_PRODUCT_DVD; if (revision == NULL || strlen(revision) == 0) revision = DEFAULT_LU_REVISION_DVD; if (serial == NULL || strlen(serial) == 0) { snprintf(buf, sizeof buf, "%.8d", 10000000 + nbs + lu->num); serial = (const char *) &buf[0]; } break; case ISTGT_LU_TYPE_TAPE: if (vendor == NULL || strlen(vendor) == 0) vendor = DEFAULT_LU_VENDOR_TAPE; if (product == NULL || strlen(product) == 0) product = DEFAULT_LU_PRODUCT_TAPE; if (revision == NULL || strlen(revision) == 0) revision = DEFAULT_LU_REVISION_TAPE; if (serial == NULL || strlen(serial) == 0) { #ifdef USE_LU_TAPE_DLT8000 snprintf(buf, sizeof buf, "CX%.8d", 10000000 + nbs + lu->num); #else snprintf(buf, sizeof buf, "%.8d", 10000000 + nbs + lu->num); #endif /* USE_LU_TAPE_DLT8000 */ serial = (const char *) &buf[0]; } break; default: if (vendor == NULL || strlen(vendor) == 0) vendor = DEFAULT_LU_VENDOR; if (product == NULL || strlen(product) == 0) product = DEFAULT_LU_PRODUCT; if (revision == NULL || strlen(revision) == 0) revision = DEFAULT_LU_REVISION; if (serial == NULL || strlen(serial) == 0) { snprintf(buf, sizeof buf, "%.8d", 10000000 + nbs + lu->num); serial = (const char *) &buf[0]; } break; } lu->inq_vendor = xstrdup(vendor); lu->inq_product = xstrdup(product); lu->inq_revision = xstrdup(revision); lu->inq_serial = xstrdup(serial); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "UnitInquiry %s %s %s %s\n", lu->inq_vendor, lu->inq_product, lu->inq_revision, lu->inq_serial); val = istgt_get_val(sp, "BlockLength"); if (val == NULL) { switch (lu->type) { case ISTGT_LU_TYPE_DISK: lu->blocklen = DEFAULT_LU_BLOCKLEN_DISK; break; case ISTGT_LU_TYPE_DVD: lu->blocklen = DEFAULT_LU_BLOCKLEN_DVD; break; case ISTGT_LU_TYPE_TAPE: lu->blocklen = DEFAULT_LU_BLOCKLEN_TAPE; break; default: lu->blocklen = DEFAULT_LU_BLOCKLEN; break; } } else { lu->blocklen = (int) strtol(val, NULL, 10); } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "BlockLength %d\n", lu->blocklen); val = istgt_get_val(sp, "QueueDepth"); if (val == NULL) { switch (lu->type) { case ISTGT_LU_TYPE_DISK: lu->queue_depth = DEFAULT_LU_QUEUE_DEPTH; //lu->queue_depth = 0; break; case ISTGT_LU_TYPE_DVD: case ISTGT_LU_TYPE_TAPE: default: lu->queue_depth = 0; break; } } else { lu->queue_depth = (int) strtol(val, NULL, 10); } if (lu->queue_depth < 0 || lu->queue_depth >= MAX_LU_QUEUE_DEPTH) { ISTGT_ERRLOG("LU%d: queue depth range error\n"); goto error_return; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "QueueDepth %d\n", lu->queue_depth); lu->maxlun = 0; for (i = 0; i < MAX_LU_LUN; i++) { lu->lun[i].type = ISTGT_LU_LUN_TYPE_NONE; lu->lun[i].rotationrate = DEFAULT_LU_ROTATIONRATE; lu->lun[i].formfactor = DEFAULT_LU_FORMFACTOR; lu->lun[i].readcache = 1; lu->lun[i].writecache = 1; lu->lun[i].serial = NULL; lu->lun[i].spec = NULL; snprintf(buf, sizeof buf, "LUN%d", i); val = istgt_get_val(sp, buf); if (val == NULL) continue; if (i != 0) { /* default LUN serial (except LUN0) */ snprintf(buf2, sizeof buf2, "%sL%d", lu->inq_serial, i); lu->lun[i].serial = xstrdup(buf2); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LUN%d Serial %s (default)\n", i, buf2); } for (j = 0; ; j++) { val = istgt_get_nmval(sp, buf, j, 0); if (val == NULL) break; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LUN%d\n", i); if (strcasecmp(val, "Device") == 0) { if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_NONE) { ISTGT_ERRLOG("LU%d: duplicate LUN%d\n", lu->num, i); goto error_return; } lu->lun[i].type = ISTGT_LU_LUN_TYPE_DEVICE; file = istgt_get_nmval(sp, buf, j, 1); if (file == NULL) { ISTGT_ERRLOG("LU%d: LUN%d: format error\n", lu->num, i); goto error_return; } lu->lun[i].u.device.file = xstrdup(file); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Device file=%s\n", lu->lun[i].u.device.file); } else if (strcasecmp(val, "Storage") == 0) { if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_NONE) { ISTGT_ERRLOG("LU%d: duplicate LUN%d\n", lu->num, i); goto error_return; } lu->lun[i].type = ISTGT_LU_LUN_TYPE_STORAGE; file = istgt_get_nmval(sp, buf, j, 1); size = istgt_get_nmval(sp, buf, j, 2); if (file == NULL || size == NULL) { ISTGT_ERRLOG("LU%d: LUN%d: format error\n", lu->num, i); goto error_return; } if (strcasecmp(size, "Auto") == 0 || strcasecmp(size, "Size") == 0) { lu->lun[i].u.storage.size = istgt_lu_get_filesize(file); } else { lu->lun[i].u.storage.size = istgt_lu_parse_size(size); } if (lu->lun[i].u.storage.size == 0) { ISTGT_ERRLOG("LU%d: LUN%d: Auto size error (%s)\n", lu->num, i, file); goto error_return; } lu->lun[i].u.storage.fd = -1; lu->lun[i].u.storage.file = xstrdup(file); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Storage file=%s, size=%"PRIu64"\n", lu->lun[i].u.storage.file, lu->lun[i].u.storage.size); } else if (strcasecmp(val, "Removable") == 0) { if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_NONE) { ISTGT_ERRLOG("LU%d: duplicate LUN%d\n", lu->num, i); goto error_return; } lu->lun[i].type = ISTGT_LU_LUN_TYPE_REMOVABLE; flags = istgt_get_nmval(sp, buf, j, 1); file = istgt_get_nmval(sp, buf, j, 2); size = istgt_get_nmval(sp, buf, j, 3); if (flags == NULL || file == NULL || size == NULL) { ISTGT_ERRLOG("LU%d: LUN%d: format error\n", lu->num, i); goto error_return; } mflags = istgt_lu_parse_media_flags(flags); msize = istgt_lu_parse_media_size(file, size, &mflags); if (msize == 0 && strcasecmp(file, "/dev/null") == 0) { /* empty media */ } else if (msize == 0) { ISTGT_ERRLOG("LU%d: LUN%d: format error\n", lu->num, i); goto error_return; } lu->lun[i].u.removable.type = 0; lu->lun[i].u.removable.id = 0; lu->lun[i].u.removable.fd = -1; lu->lun[i].u.removable.flags = mflags; lu->lun[i].u.removable.file = xstrdup(file); lu->lun[i].u.removable.size = msize; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Removable file=%s, size=%"PRIu64", flags=%x\n", lu->lun[i].u.removable.file, lu->lun[i].u.removable.size, lu->lun[i].u.removable.flags); } else if (strncasecmp(val, "Slot", 4) == 0) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { lu->lun[i].u.slot.maxslot = 0; for (k = 0; k < MAX_LU_LUN_SLOT; k++) { lu->lun[i].u.slot.present[k] = 0; lu->lun[i].u.slot.flags[k] = 0; lu->lun[i].u.slot.file[k] = NULL; lu->lun[i].u.slot.size[k] = 0; } } else if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_SLOT) { ISTGT_ERRLOG("LU%d: duplicate LUN%d\n", lu->num, i); goto error_return; } lu->lun[i].type = ISTGT_LU_LUN_TYPE_SLOT; if (sscanf(val, "%*[^0-9]%d", &slot) != 1) { ISTGT_ERRLOG("LU%d: slot number error\n", lu->num); goto error_return; } if (slot < 0 || slot >= MAX_LU_LUN_SLOT) { ISTGT_ERRLOG("LU%d: slot number range error\n", lu->num); goto error_return; } if (lu->lun[i].u.slot.present[slot]) { ISTGT_ERRLOG("LU%d: duplicate slot %d\n", lu->num, slot); goto error_return; } lu->lun[i].u.slot.present[slot] = 1; if (slot + 1 > lu->lun[i].u.slot.maxslot) { lu->lun[i].u.slot.maxslot = slot + 1; } flags = istgt_get_nmval(sp, buf, j, 1); file = istgt_get_nmval(sp, buf, j, 2); size = istgt_get_nmval(sp, buf, j, 3); if (flags == NULL || file == NULL || size == NULL) { ISTGT_ERRLOG("LU%d: LUN%d: format error\n", lu->num, i); goto error_return; } mflags = istgt_lu_parse_media_flags(flags); msize = istgt_lu_parse_media_size(file, size, &mflags); if (msize == 0) { ISTGT_ERRLOG("LU%d: LUN%d: format error\n", lu->num, i); goto error_return; } lu->lun[i].u.slot.flags[slot] = mflags; lu->lun[i].u.slot.file[slot] = xstrdup(file); lu->lun[i].u.slot.size[slot] = msize; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Slot%d file=%s, size=%"PRIu64", flags=%x\n", slot, lu->lun[i].u.slot.file[slot], lu->lun[i].u.slot.size[slot], lu->lun[i].u.slot.flags[slot]); } else if (strncasecmp(val, "Option", 6) == 0) { key = istgt_get_nmval(sp, buf, j, 1); val = istgt_get_nmval(sp, buf, j, 2); if (key == NULL || val == NULL) { ISTGT_ERRLOG("LU%d: LUN%d: format error\n", lu->num, i); goto error_return; } if (strcasecmp(key, "Serial") == 0) { /* set LUN serial */ if (strlen(val) == 0) { ISTGT_ERRLOG("LU%d: LUN%d: no serial\n", lu->num, i); goto error_return; } xfree(lu->lun[i].serial); lu->lun[i].serial = xstrdup(val); } else if (strcasecmp(key, "RPM") == 0) { rpm = (int)strtol(val, NULL, 10); if (rpm < 0) { rpm = 0; } else if (rpm > 0xfffe) { rpm = 0xfffe; } lu->lun[i].rotationrate = rpm; } else if (strcasecmp(key, "FormFactor") == 0) { formfactor = (int)strtol(val, NULL, 10); if (formfactor < 0) { formfactor = 0; } else if (formfactor > 0x0f) { formfactor = 0xf; } lu->lun[i].formfactor = formfactor; } else if (strcasecmp(key, "ReadCache") == 0) { if (strcasecmp(val, "Enable") == 0) { lu->lun[i].readcache = 1; } else if (strcasecmp(val, "Disable") == 0) { lu->lun[i].readcache = 0; } else { ISTGT_ERRLOG("LU%d: LUN%d: unknown val(%s)\n", lu->num, i, val); } } else if (strcasecmp(key, "WriteCache") == 0) { if (strcasecmp(val, "Enable") == 0) { lu->lun[i].writecache = 1; } else if (strcasecmp(val, "Disable") == 0) { lu->lun[i].writecache = 0; } else { ISTGT_ERRLOG("LU%d: LUN%d: unknown val(%s)\n", lu->num, i, val); } } else { ISTGT_WARNLOG("LU%d: LUN%d: unknown key(%s)\n", lu->num, i, key); continue; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LUN%d Option %s => %s\n", i, key, val); continue; } else { ISTGT_ERRLOG("LU%d: unknown lun type\n", lu->num); goto error_return; } } if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_SLOT) { if (lu->lun[i].u.slot.maxslot == 0) { ISTGT_ERRLOG("LU%d: no slot\n", lu->num); goto error_return; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "maxslot=%d\n", lu->lun[i].u.slot.maxslot); } lu->maxlun = i + 1; } if (lu->maxlun == 0) { ISTGT_ERRLOG("LU%d: no LUN\n", lu->num); goto error_return; } if (lu->lun[0].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_ERRLOG("LU%d: no LUN0\n", lu->num); goto error_return; } /* set local values if any */ rc = istgt_lu_set_local_settings(istgt, sp, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: local setting error\n", lu->num); goto error_return; } /* tsih 0 is reserved */ for (i = 0; i < MAX_LU_TSIH; i++) { lu->tsih[i].tag = 0; lu->tsih[i].tsih = 0; lu->tsih[i].initiator_port = NULL; } lu->maxtsih = 1; lu->last_tsih = 0; MTX_LOCK(&istgt->mutex); istgt->nlogical_unit++; istgt->logical_unit[lu->num] = lu; MTX_UNLOCK(&istgt->mutex); return 0; error_return: xfree(lu->name); xfree(lu->alias); xfree(lu->inq_vendor); xfree(lu->inq_product); xfree(lu->inq_revision); for (i = 0; i < MAX_LU_LUN; i++) { switch (lu->lun[i].type) { case ISTGT_LU_LUN_TYPE_DEVICE: xfree(lu->lun[i].u.device.file); break; case ISTGT_LU_LUN_TYPE_STORAGE: xfree(lu->lun[i].u.storage.file); break; case ISTGT_LU_LUN_TYPE_REMOVABLE: xfree(lu->lun[i].u.removable.file); break; case ISTGT_LU_LUN_TYPE_SLOT: for (j = 0; j < lu->lun[i].u.slot.maxslot; j++) { xfree(lu->lun[i].u.slot.file[j]); } break; case ISTGT_LU_LUN_TYPE_NONE: default: break; } } for (i = 0; i < MAX_LU_TSIH; i++) { xfree(lu->tsih[i].initiator_port); } for (i = 0; i < lu->maxmap; i++) { /* nothing */ } xfree(lu); return -1; } static int istgt_lu_del_unit(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { int i, j; if (lu ==NULL) return 0; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "del unit %d\n", lu->num); MTX_LOCK(&istgt->mutex); istgt->nlogical_unit--; istgt->logical_unit[lu->num] = NULL; MTX_UNLOCK(&istgt->mutex); xfree(lu->name); xfree(lu->alias); xfree(lu->inq_vendor); xfree(lu->inq_product); xfree(lu->inq_revision); xfree(lu->inq_serial); for (i = 0; i < MAX_LU_LUN; i++) { xfree(lu->lun[i].serial); switch (lu->lun[i].type) { case ISTGT_LU_LUN_TYPE_DEVICE: xfree(lu->lun[i].u.device.file); break; case ISTGT_LU_LUN_TYPE_STORAGE: xfree(lu->lun[i].u.storage.file); break; case ISTGT_LU_LUN_TYPE_REMOVABLE: xfree(lu->lun[i].u.removable.file); break; case ISTGT_LU_LUN_TYPE_SLOT: for (j = 0; j < lu->lun[i].u.slot.maxslot; j++) { xfree(lu->lun[i].u.slot.file[j]); } break; case ISTGT_LU_LUN_TYPE_NONE: default: break; } } for (i = 0; i < MAX_LU_TSIH; i++) { xfree(lu->tsih[i].initiator_port); } for (i = 0; i < lu->maxmap; i++) { /* nothing */ } return 0; } static void *luworker(void *arg); int istgt_lu_init(ISTGT_Ptr istgt) { ISTGT_LU_Ptr lu; CF_SECTION *sp; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_init\n"); sp = istgt_find_cf_section(istgt->config, "Global"); if (sp == NULL) { ISTGT_ERRLOG("find_cf_section failed()\n"); return -1; } sp = istgt->config->section; while (sp != NULL) { if (sp->type == ST_LOGICAL_UNIT) { if (sp->num == 0) { ISTGT_ERRLOG("Unit 0 is invalid\n"); return -1; } if (sp->num > ISTGT_LU_TAG_MAX) { ISTGT_ERRLOG("tag %d is invalid\n", sp->num); return -1; } rc = istgt_lu_add_unit(istgt, sp); if (rc < 0) { ISTGT_ERRLOG("lu_add_unit() failed\n"); return -1; } } sp = sp->next; } for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = istgt->logical_unit[i]; if (lu == NULL) continue; rc = pthread_mutex_init(&lu->mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num); return -1; } rc = pthread_mutex_init(&lu->state_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num); return -1; } rc = pthread_mutex_init(&lu->queue_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_init() failed\n", lu->num); return -1; } rc = pthread_cond_init(&lu->queue_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: cond_init() failed\n", lu->num); return -1; } switch (lu->type) { case ISTGT_LU_TYPE_PASS: rc = istgt_lu_pass_init(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_pass_init() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DISK: rc = istgt_lu_disk_init(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_init() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DVD: rc = istgt_lu_dvd_init(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_dvd_init() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_TAPE: rc = istgt_lu_tape_init(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_tape_init() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_NONE: //ISTGT_ERRLOG("LU%d: dummy type\n", lu->num); break; default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } istgt_lu_set_state(lu, ISTGT_STATE_INITIALIZED); } return 0; } int istgt_lu_set_all_state(ISTGT_Ptr istgt, ISTGT_STATE state) { ISTGT_LU_Ptr lu; int i; for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = istgt->logical_unit[i]; if (lu == NULL) continue; istgt_lu_set_state(lu, state); } return 0; } int istgt_lu_create_threads(ISTGT_Ptr istgt) { #ifdef HAVE_PTHREAD_SET_NAME_NP char buf[MAX_TMPBUF]; #endif ISTGT_LU_Ptr lu; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_create_threads\n"); for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = istgt->logical_unit[i]; if (lu == NULL) continue; if (lu->queue_depth != 0) { /* create LU thread */ #ifdef ISTGT_STACKSIZE rc = pthread_create(&lu->thread, &istgt->attr, &luworker, (void *)lu); #else rc = pthread_create(&lu->thread, NULL, &luworker, (void *)lu); #endif if (rc != 0) { ISTGT_ERRLOG("pthread_create() failed\n"); return -1; } #if 0 rc = pthread_detach(lu->thread); if (rc != 0) { ISTGT_ERRLOG("pthread_detach() failed\n"); return -1; } #endif #ifdef HAVE_PTHREAD_SET_NAME_NP snprintf(buf, sizeof buf, "luthread #%d", i); pthread_set_name_np(lu->thread, buf); #endif } } return 0; } int istgt_lu_shutdown(ISTGT_Ptr istgt) { ISTGT_LU_Ptr lu; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_shutdown\n"); for (i = 0; i < MAX_LOGICAL_UNIT; i++) { lu = istgt->logical_unit[i]; if (lu == NULL) continue; istgt_lu_set_state(lu, ISTGT_STATE_SHUTDOWN); switch (lu->type) { case ISTGT_LU_TYPE_PASS: rc = istgt_lu_pass_shutdown(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_pass_shutdown() failed\n", lu->num); /* ignore error */ } break; case ISTGT_LU_TYPE_DISK: rc = istgt_lu_disk_shutdown(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_shutdown() failed\n", lu->num); /* ignore error */ } break; case ISTGT_LU_TYPE_DVD: rc = istgt_lu_dvd_shutdown(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_dvd_shutdown() failed\n", lu->num); /* ignore error */ } break; case ISTGT_LU_TYPE_TAPE: rc = istgt_lu_tape_shutdown(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_tape_shutdown() failed\n", lu->num); /* ignore error */ } break; case ISTGT_LU_TYPE_NONE: //ISTGT_ERRLOG("LU%d: dummy type\n", lu->num); break; default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } rc = istgt_lu_del_unit(istgt, lu); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_del_unit() failed\n", lu->num); /* ignore error */ } if (lu->queue_depth != 0) { rc = pthread_cond_broadcast(&lu->queue_cond); if (rc != 0) { ISTGT_ERRLOG("LU%d: cond_broadcast() failed\n", lu->num); } rc = pthread_join(lu->thread, NULL); if (rc != 0) { ISTGT_ERRLOG("LU%d: pthread_join() failed\n", lu->num); } } rc = pthread_cond_destroy(&lu->queue_cond); if (rc != 0) { ISTGT_ERRLOG("LU%d: cond_destroy() failed\n", lu->num); /* ignore error */ } rc = pthread_mutex_destroy(&lu->queue_mutex); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num); /* ignore error */ } rc = pthread_mutex_destroy(&lu->state_mutex); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num); /* ignore error */ } rc = pthread_mutex_destroy(&lu->mutex); if (rc != 0) { ISTGT_ERRLOG("LU%d: mutex_destroy() failed\n", lu->num); /* ignore error */ } xfree(lu); istgt->logical_unit[i] = NULL; } return 0; } int istgt_lu_islun2lun(uint64_t islun) { uint64_t fmt_lun; uint64_t method; int lun_i; fmt_lun = islun; method = (fmt_lun >> 62) & 0x03U; fmt_lun = fmt_lun >> 48; if (method == 0x00U) { lun_i = (int) (fmt_lun & 0x00ffU); } else if (method == 0x01U) { lun_i = (int) (fmt_lun & 0x3fffU); } else { lun_i = 0xffffU; } return lun_i; } uint64_t istgt_lu_lun2islun(int lun, int maxlun) { uint64_t fmt_lun; uint64_t method; uint64_t islun; islun = (uint64_t) lun; if (maxlun <= 0x0100) { /* below 256 */ method = 0x00U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (islun & 0x00ffU) << 48; } else if (maxlun <= 0x4000U) { /* below 16384 */ method = 0x01U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (islun & 0x3fffU) << 48; } else { /* XXX */ fmt_lun = ~((uint64_t) 0); } return fmt_lun; } int istgt_lu_reset(ISTGT_LU_Ptr lu, uint64_t lun) { int lun_i; int rc; if (lu == NULL) return -1; lun_i = istgt_lu_islun2lun(lun); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: Name=%s, LUN=%d\n", lu->num, lu->name, lun_i); switch (lu->type) { case ISTGT_LU_TYPE_PASS: MTX_LOCK(&lu->mutex); rc = istgt_lu_pass_reset(lu, lun_i); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_pass_reset() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DISK: MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_reset(lu, lun_i); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_reset() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_reset(lu, lun_i); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_dvd_reset() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_reset(lu, lun_i); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_tape_reset() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_NONE: //ISTGT_ERRLOG("LU%d: dummy type\n", lu->num); break; default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } return 0; } int istgt_lu_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_Ptr lu; int rc; if (lu_cmd == NULL) return -1; lu = lu_cmd->lu; if (lu == NULL) return -1; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: PG=0x%4.4x, Name=%s, LUN=0x%16.16"PRIx64"\n", lu->num, conn->portal.tag, lu->name, lu_cmd->lun); if (lu->online == 0) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LU%d: offline\n", lu->num); /* LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE */ lu_cmd->sense_data_len = istgt_lu_scsi_build_sense_data(lu_cmd->sense_data, ISTGT_SCSI_SENSE_NOT_READY, 0x04, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } rc = 0; switch (lu->type) { case ISTGT_LU_TYPE_PASS: MTX_LOCK(&lu->mutex); rc = istgt_lu_pass_execute(conn, lu_cmd); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_pass_execute() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DISK: if (lu->queue_depth != 0) { //MTX_LOCK(&lu->queue_mutex); rc = istgt_lu_disk_queue(conn, lu_cmd); //MTX_UNLOCK(&lu->queue_mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_queue() failed\n", lu->num); return -1; } } else { MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_execute(conn, lu_cmd); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_execute() failed\n", lu->num); return -1; } } break; case ISTGT_LU_TYPE_DVD: MTX_LOCK(&lu->mutex); rc = istgt_lu_dvd_execute(conn, lu_cmd); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_dvd_execute() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_TAPE: MTX_LOCK(&lu->mutex); rc = istgt_lu_tape_execute(conn, lu_cmd); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_tape_execute() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_NONE: //ISTGT_ERRLOG("LU%d: dummy type\n", lu->num); break; default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } return rc; } int istgt_lu_create_task(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, ISTGT_LU_TASK_Ptr lu_task, int lun) { ISCSI_PDU_Ptr dst_pdu, src_pdu; uint8_t *cdb; int alloc_len; #if 0 int rc; #endif if (lu_task == NULL) return -1; lu_task->type = ISTGT_LU_TASK_RESPONSE; lu_task->conn = conn; strncpy(lu_task->initiator_name, conn->initiator_name, sizeof lu_task->initiator_name); strncpy(lu_task->initiator_port, conn->initiator_port, sizeof lu_task->initiator_port); lu_task->lun = (int) lun; lu_task->use_cond = 0; lu_task->dup_iobuf = 0; lu_task->iobuf = NULL; lu_task->data = NULL; lu_task->sense_data = NULL; lu_task->create_time = 0; lu_task->condwait = 0; lu_task->offset = 0; lu_task->req_execute = 0; lu_task->req_transfer_out = 0; lu_task->error = 0; lu_task->abort = 0; lu_task->execute = 0; lu_task->complete = 0; lu_task->lock = 0; #if 0 rc = pthread_mutex_init(&lu_task->trans_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); return -1; } rc = pthread_cond_init(&lu_task->trans_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); return -1; } rc = pthread_cond_init(&lu_task->exec_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); return -1; } #endif lu_task->lu_cmd.pdu = xmalloc(sizeof *lu_task->lu_cmd.pdu); memset(lu_task->lu_cmd.pdu, 0, sizeof *lu_task->lu_cmd.pdu); /* copy PDU */ dst_pdu = lu_task->lu_cmd.pdu; src_pdu = lu_cmd->pdu; memcpy(&dst_pdu->bhs, &src_pdu->bhs, ISCSI_BHS_LEN); dst_pdu->ahs = src_pdu->ahs; memcpy(dst_pdu->header_digest, src_pdu->header_digest, ISCSI_DIGEST_LEN); if (src_pdu->data == src_pdu->shortdata) { memcpy(dst_pdu->shortdata, src_pdu->shortdata, sizeof src_pdu->shortdata); dst_pdu->data = dst_pdu->shortdata; } else { dst_pdu->data = src_pdu->data; } memcpy(dst_pdu->data_digest, src_pdu->data_digest, ISCSI_DIGEST_LEN); dst_pdu->total_ahs_len = src_pdu->total_ahs_len; dst_pdu->data_segment_len = src_pdu->data_segment_len; dst_pdu->copy_pdu = 0; src_pdu->copy_pdu = 1; /* copy other lu_cmd */ lu_task->lu_cmd.lu = lu_cmd->lu; cdb = ((uint8_t *) &lu_task->lu_cmd.pdu->bhs) + 32; lu_task->lu_cmd.I_bit = lu_cmd->I_bit; lu_task->lu_cmd.F_bit = lu_cmd->F_bit; lu_task->lu_cmd.R_bit = lu_cmd->R_bit; lu_task->lu_cmd.W_bit = lu_cmd->W_bit; lu_task->lu_cmd.Attr_bit = lu_cmd->Attr_bit; lu_task->lu_cmd.lun = lu_cmd->lun; lu_task->lu_cmd.task_tag = lu_cmd->task_tag; lu_task->lu_cmd.transfer_len = lu_cmd->transfer_len; //lu_task->lu_cmd.cdb = lu_cmd->cdb; lu_task->lu_cmd.cdb = cdb; lu_task->lu_cmd.CmdSN = lu_cmd->CmdSN; //lu_task->lu_cmd.iobuf = lu_cmd->iobuf; lu_task->lu_cmd.iobuf = NULL; lu_task->lu_cmd.iobufsize = lu_cmd->iobufsize; lu_task->lu_cmd.data = lu_cmd->data; lu_task->lu_cmd.data_len = lu_cmd->data_len; lu_task->lu_cmd.alloc_len = lu_cmd->alloc_len; lu_task->lu_cmd.status = lu_cmd->status; lu_task->lu_cmd.sense_data = lu_cmd->sense_data; lu_task->lu_cmd.sense_data_len = lu_cmd->sense_data_len; lu_task->lu_cmd.sense_alloc_len = lu_cmd->sense_alloc_len; /* pre allocate buffer */ lu_task->lu_cmd.iobufsize = lu_cmd->transfer_len + 65536; #if 0 lu_task->data = xmalloc(lu_cmd->alloc_len); lu_task->sense_data = xmalloc(lu_cmd->sense_alloc_len); lu_task->iobuf = xmalloc(lu_task->lu_cmd.iobufsize); #else alloc_len = ISCSI_ALIGN(lu_cmd->alloc_len); alloc_len += ISCSI_ALIGN(lu_cmd->sense_alloc_len); alloc_len += ISCSI_ALIGN(lu_task->lu_cmd.iobufsize); lu_task->data = xmalloc(alloc_len); lu_task->sense_data = lu_task->data + ISCSI_ALIGN(lu_cmd->alloc_len); lu_task->iobuf = lu_task->sense_data + ISCSI_ALIGN(lu_cmd->sense_alloc_len); #endif /* creation time */ lu_task->create_time = time(NULL); /* wait time */ lu_task->condwait = conn->timeout * 1000; if (lu_task->condwait < ISTGT_CONDWAIT_MIN) { lu_task->condwait = ISTGT_CONDWAIT_MIN; } return 0; } int istgt_lu_destroy_task(ISTGT_LU_TASK_Ptr lu_task) { int rc; if (lu_task == NULL) return -1; if (lu_task->use_cond != 0) { rc = pthread_mutex_destroy(&lu_task->trans_mutex); if (rc != 0) { ISTGT_ERRLOG("mutex_destroy() failed\n"); return -1; } rc = pthread_cond_destroy(&lu_task->trans_cond); if (rc != 0) { ISTGT_ERRLOG("cond_destroy() failed\n"); return -1; } rc = pthread_cond_destroy(&lu_task->exec_cond); if (rc != 0) { ISTGT_ERRLOG("cond_destroy() failed\n"); return -1; } } if (lu_task->lu_cmd.pdu != NULL) { if (lu_task->lu_cmd.pdu->copy_pdu == 0) { xfree(lu_task->lu_cmd.pdu->ahs); if (lu_task->lu_cmd.pdu->data != lu_task->lu_cmd.pdu->shortdata) { xfree(lu_task->lu_cmd.pdu->data); } } xfree(lu_task->lu_cmd.pdu); } #if 0 if (lu_task->dup_iobuf == 0) { xfree(lu_task->iobuf); } xfree(lu_task->data); xfree(lu_task->sense_data); #else xfree(lu_task->data); #endif xfree(lu_task); return 0; } int istgt_lu_clear_task_IT(CONN_Ptr conn, ISTGT_LU_Ptr lu) { int rc; if (lu == NULL) return -1; if (lu->queue_depth == 0) return 0; rc = 0; switch (lu->type) { case ISTGT_LU_TYPE_DISK: MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_queue_clear_IT(conn, lu); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_queue_clear_IT() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DVD: case ISTGT_LU_TYPE_TAPE: case ISTGT_LU_TYPE_NONE: default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } return 0; } int istgt_lu_clear_task_ITL(CONN_Ptr conn, ISTGT_LU_Ptr lu, uint64_t lun) { int lun_i; int rc; if (lu == NULL) return -1; if (lu->queue_depth == 0) return 0; lun_i = istgt_lu_islun2lun(lun); rc = 0; switch (lu->type) { case ISTGT_LU_TYPE_DISK: MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_queue_clear_ITL(conn, lu, lun_i); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_queue_clear_ITL() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DVD: case ISTGT_LU_TYPE_TAPE: case ISTGT_LU_TYPE_NONE: default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } return 0; } int istgt_lu_clear_task_ITLQ(CONN_Ptr conn, ISTGT_LU_Ptr lu, uint64_t lun, uint32_t CmdSN) { int lun_i; int rc; if (lu == NULL) return -1; if (lu->queue_depth == 0) return 0; lun_i = istgt_lu_islun2lun(lun); rc = 0; switch (lu->type) { case ISTGT_LU_TYPE_DISK: MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_queue_clear_ITLQ(conn, lu, lun_i, CmdSN); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_queue_clear_ITLQ() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DVD: case ISTGT_LU_TYPE_TAPE: case ISTGT_LU_TYPE_NONE: default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } return 0; } int istgt_lu_clear_all_task(ISTGT_LU_Ptr lu, uint64_t lun) { int rc; if (lu == NULL) return -1; if (lu->queue_depth == 0) return 0; rc = 0; switch (lu->type) { case ISTGT_LU_TYPE_DISK: MTX_LOCK(&lu->mutex); rc = istgt_lu_disk_queue_clear_all(lu, lun); MTX_UNLOCK(&lu->mutex); if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_queue_clear_all() failed\n", lu->num); return -1; } break; case ISTGT_LU_TYPE_DVD: case ISTGT_LU_TYPE_TAPE: case ISTGT_LU_TYPE_NONE: default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } return 0; } static void * luworker(void *arg) { ISTGT_LU_Ptr lu = (ISTGT_LU_Ptr) arg; sigset_t signew, sigold; #if 0 struct timespec abstime; time_t now; int timeout = 20; /* XXX */ #endif int qcnt; int lun; int rc; sigemptyset(&signew); sigemptyset(&sigold); sigaddset(&signew, ISTGT_SIGWAKEUP); pthread_sigmask(SIG_UNBLOCK, &signew, &sigold); while (istgt_get_state(lu->istgt) != ISTGT_STATE_RUNNING) { if (istgt_get_state(lu->istgt) == ISTGT_STATE_EXITING || istgt_get_state(lu->istgt) == ISTGT_STATE_SHUTDOWN) { ISTGT_ERRLOG("exit before running\n"); return NULL; } //ISTGT_WARNLOG("Wait for running\n"); sleep(1); continue; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d loop start\n", lu->num); lun = 0; qcnt = 0; #if 0 memset(&abstime, 0, sizeof abstime); #endif while (1) { switch (lu->type) { case ISTGT_LU_TYPE_DISK: while (1) { if (istgt_lu_get_state(lu) != ISTGT_STATE_RUNNING) { goto loop_exit; } MTX_LOCK(&lu->queue_mutex); qcnt = istgt_lu_disk_queue_count(lu, &lun); if (qcnt == 0) { #if 0 now = time(NULL); abstime.tv_sec = now + timeout; abstime.tv_nsec = 0; rc = pthread_cond_timedwait(&lu->queue_cond, &lu->queue_mutex, &abstime); if (rc == ETIMEDOUT) { /* nothing */ } #else pthread_cond_wait(&lu->queue_cond, &lu->queue_mutex); #endif qcnt = istgt_lu_disk_queue_count(lu, &lun); if (qcnt == 0) { MTX_UNLOCK(&lu->queue_mutex); continue; } } MTX_UNLOCK(&lu->queue_mutex); break; } if (qcnt < 0) { ISTGT_ERRLOG("LU%d: lu_disk_queue_count() failed\n", lu->num); break; } rc = istgt_lu_disk_queue_start(lu, lun); lun++; if (rc == -2) { ISTGT_WARNLOG("LU%d: lu_disk_queue_start() aborted\n", lu->num); break; } if (rc < 0) { ISTGT_ERRLOG("LU%d: lu_disk_queue_start() failed\n", lu->num); break; } break; case ISTGT_LU_TYPE_DVD: case ISTGT_LU_TYPE_TAPE: case ISTGT_LU_TYPE_NONE: default: ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return NULL; } #if 0 /* still running? */ if (qcnt <= 1) { if (istgt_lu_get_state(lu) != ISTGT_STATE_RUNNING) { goto loop_exit; } } #endif } loop_exit: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d loop ended\n", lu->num); return NULL; } istgt-20111008/src/istgt_scsi.h000644 000000 000000 00000024040 11635327750 016325 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_SCSI_H #define ISTGT_SCSI_H typedef enum { ISTGT_SCSI_STATUS_GOOD = 0x00, ISTGT_SCSI_STATUS_CHECK_CONDITION = 0x02, ISTGT_SCSI_STATUS_CONDITION_MET = 0x04, ISTGT_SCSI_STATUS_BUSY = 0x08, ISTGT_SCSI_STATUS_INTERMEDIATE = 0x10, ISTGT_SCSI_STATUS_INTERMEDIATE_CONDITION_MET = 0x14, ISTGT_SCSI_STATUS_RESERVATION_CONFLICT = 0x18, ISTGT_SCSI_STATUS_Obsolete = 0x22, ISTGT_SCSI_STATUS_TASK_SET_FULL = 0x28, ISTGT_SCSI_STATUS_ACA_ACTIVE = 0x30, ISTGT_SCSI_STATUS_TASK_ABORTED = 0x40, } ISTGT_SCSI_STATUS; typedef enum { ISTGT_SCSI_SENSE_NO_SENSE = 0x00, ISTGT_SCSI_SENSE_RECOVERED_ERROR =0x01, ISTGT_SCSI_SENSE_NOT_READY = 0x02, ISTGT_SCSI_SENSE_MEDIUM_ERROR = 0x03, ISTGT_SCSI_SENSE_HARDWARE_ERROR = 0x04, ISTGT_SCSI_SENSE_ILLEGAL_REQUEST = 0x05, ISTGT_SCSI_SENSE_UNIT_ATTENTION = 0x06, ISTGT_SCSI_SENSE_DATA_PROTECT = 0x07, ISTGT_SCSI_SENSE_BLANK_CHECK = 0x08, ISTGT_SCSI_SENSE_VENDOR_SPECIFIC = 0x09, ISTGT_SCSI_SENSE_COPY_ABORTED = 0x0a, ISTGT_SCSI_SENSE_ABORTED_COMMAND = 0x0b, ISTGT_SCSI_SENSE_VOLUME_OVERFLOW = 0x0d, ISTGT_SCSI_SENSE_MISCOMPARE = 0x0e, } ISTGT_SCSI_SENSE; typedef enum { /* SPC3 related */ SPC_ACCESS_CONTROL_IN = 0x86, SPC_ACCESS_CONTROL_OUT = 0x87, //SPC_CHANGE_ALIASES = 0x000b00a4, SPC_EXTENDED_COPY = 0x83, SPC_INQUIRY = 0x12, SPC_LOG_SELECT = 0x4c, SPC_LOG_SENSE = 0x4d, SPC_MODE_SELECT_6 = 0x15, SPC_MODE_SELECT_10 = 0x55, SPC_MODE_SENSE_6 = 0x1a, SPC_MODE_SENSE_10 = 0x5a, SPC_PERSISTENT_RESERVE_IN = 0x5e, SPC_PERSISTENT_RESERVE_OUT = 0x5f, SPC_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e, SPC_READ_ATTRIBUTE = 0x8c, SPC_READ_BUFFER = 0x3c, //SPC_READ_MEDIA_SERIAL_NUMBER = 0x000100ab, SPC_RECEIVE_COPY_RESULTS = 0x84, SPC_RECEIVE_DIAGNOSTIC_RESULTS = 0x1c, //SPC_REPORT_ALIASES = 0x000b00a3, //SPC_REPORT_DEVICE_IDENTIFIER = 0x000500a3, SPC_REPORT_LUNS = 0xa0, //SPC_REPORT_PRIORITY = 0x000e00a3, //SPC_REPORT_SUPPORTED_OPERATION_CODES = 0x000c00a3, //SPC_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS = 0x000d00a3, //SPC_REPORT_TARGET_PORT_GROUPS = 0x000a00a3, //SPC_REPORT_TIMESTAMP = 0x000f00a3, SPC_REQUEST_SENSE = 0x03, SPC_SEND_DIAGNOSTIC = 0x1d, //SPC_SET_DEVICE_IDENTIFIER = 0x000600a4, //SPC_SET_PRIORITY = 0x000e00a4, //SPC_SET_TARGET_PORT_GROUPS = 0x000a00a4, //SPC_SET_TIMESTAMP = 0x000f00a4, SPC_TEST_UNIT_READY = 0x00, SPC_WRITE_ATTRIBUTE = 0x8d, SPC_WRITE_BUFFER = 0x3b, SPC_SERVICE_ACTION_IN_12 = 0xab, SPC_SERVICE_ACTION_OUT_12 = 0xa9, SPC_SERVICE_ACTION_IN_16 = 0x9e, SPC_SERVICE_ACTION_OUT_16 = 0x9f, SPC_VARIABLE_LENGTH = 0x7f, SPC_MO_CHANGE_ALIASES = 0x0b, SPC_MO_SET_DEVICE_IDENTIFIER = 0x06, SPC_MO_SET_PRIORITY = 0x0e, SPC_MO_SET_TARGET_PORT_GROUPS = 0x0a, SPC_MO_SET_TIMESTAMP = 0x0f, SPC_MI_REPORT_ALIASES = 0x0b, SPC_MI_REPORT_DEVICE_IDENTIFIER = 0x05, SPC_MI_REPORT_PRIORITY = 0x0e, SPC_MI_REPORT_SUPPORTED_OPERATION_CODES = 0x0c, SPC_MI_REPORT_SUPPORTED_TASK_MANAGEMENT_FUNCTIONS = 0x0d, SPC_MI_REPORT_TARGET_PORT_GROUPS = 0x0a, SPC_MI_REPORT_TIMESTAMP = 0x0f, /* SPC2 related (Obsolete) */ SPC2_RELEASE_6 = 0x17, SPC2_RELEASE_10 = 0x57, SPC2_RESERVE_6 = 0x16, SPC2_RESERVE_10 = 0x56, } SPC_OPCODE; typedef enum { SCC_MAINTENANCE_IN = 0xa3, SCC_MAINTENANCE_OUT = 0xa4, } SCC_OPCODE; typedef enum { SBC_COMPARE_AND_WRITE = 0x89, SBC_FORMAT_UNIT = 0x04, SBC_GET_LBA_STATUS = 0x0012009e, SBC_ORWRITE_16 = 0x8b, //SBC_ORWRITE_32 = 0x000e007f, SBC_PRE_FETCH_10 = 0x34, SBC_PRE_FETCH_16 = 0x90, SBC_READ_6 = 0x08, SBC_READ_10 = 0x28, SBC_READ_12 = 0xa8, SBC_READ_16 = 0x88, //SBC_READ_32 = 0x0009007f, SBC_READ_ATTRIBUTE = 0x8c, SBC_READ_BUFFER = 0x3c, SBC_READ_CAPACITY_10 = 0x25, //SBC_READ_CAPACITY_16 = 0x0010009e, SBC_READ_DEFECT_DATA_10 = 0x37, SBC_READ_DEFECT_DATA_12 = 0xb7, SBC_READ_LONG_10 = 0x3e, //SBC_READ_LONG_16 = 0x0011009e, SBC_REASSIGN_BLOCKS = 0x07, //SBC_REPORT_REFERRALS = 0x0013009e, SBC_SANITIZE = 0x48, SBC_START_STOP_UNIT = 0x1b, SBC_SYNCHRONIZE_CACHE_10 = 0x35, SBC_SYNCHRONIZE_CACHE_16 = 0x91, SBC_UNMAP = 0x42, SBC_VERIFY_10 = 0x2f, SBC_VERIFY_12 = 0xaf, SBC_VERIFY_16 = 0x8f, //SBC_VERIFY_32 = 0x000a007f, SBC_WRITE_6 = 0x0a, SBC_WRITE_10 = 0x2a, SBC_WRITE_12 = 0xaa, SBC_WRITE_16 = 0x8a, //SBC_WRITE_32 = 0x000b007f, SBC_WRITE_AND_VERIFY_10 = 0x2e, SBC_WRITE_AND_VERIFY_12 = 0xae, SBC_WRITE_AND_VERIFY_16 = 0x8e, //SBC_WRITE_AND_VERIFY_32 = 0x000c007f, SBC_WRITE_LONG_10 = 0x3f, //SBC_WRITE_LONG_16 = 0x0011009f, SBC_WRITE_SAME_10 = 0x41, SBC_WRITE_SAME_16 = 0x93, //SBC_WRITE_SAME_32 = 0x000d007f, SBC_XDREAD_10 = 0x52, //SBC_XDREAD_32 = 0x0003007f, SBC_XDWRITE_10 = 0x50, //SBC_XDWRITE_32 = 0x0004007f, SBC_XDWRITEREAD_10 = 0x53, //SBC_XDWRITEREAD_32 = 0x0007007f, SBC_XPWRITE_10 = 0x51, //SBC_XPWRITE_32 = 0x0006007f, SBC_SAI_READ_CAPACITY_16 = 0x10, SBC_SAI_READ_LONG_16 = 0x11, SBC_SAO_WRITE_LONG_16 = 0x11, SBC_VL_READ_32 = 0x0009, SBC_VL_VERIFY_32 = 0x000a, SBC_VL_WRITE_32 = 0x000b, SBC_VL_WRITE_AND_VERIFY_32 = 0x000c, SBC_VL_WRITE_SAME_32 = 0x000d, SBC_VL_XDREAD_32 = 0x0003, SBC_VL_XDWRITE_32 = 0x0004, SBC_VL_XDWRITEREAD_32 = 0x0007, SBC_VL_XPWRITE_32 = 0x0006, } SBC_OPCODE; typedef enum { /* MMC6 */ MMC_READ_DISC_STRUCTURE = 0xad, /* MMC4 */ MMC_BLANK = 0xa1, MMC_CLOSE_TRACK_SESSION = 0x5b, MMC_ERASE_10 = 0x2c, MMC_FORMAT_UNIT = 0x04, MMC_GET_CONFIGURATION = 0x46, MMC_GET_EVENT_STATUS_NOTIFICATION = 0x4a, MMC_GET_PERFORMANCE = 0xac, MMC_INQUIRY = 0x12, MMC_LOAD_UNLOAD_MEDIUM = 0xa6, MMC_MECHANISM_STATUS = 0xbd, MMC_MODE_SELECT_10 = 0x55, MMC_MODE_SENSE_10 = 0x5a, MMC_PAUSE_RESUME = 0x4b, MMC_PLAY_AUDIO_10 = 0x45, MMC_PLAY_AUDIO_12 = 0xa5, MMC_PLAY_AUDIO_MSF = 0x47, MMC_PREVENT_ALLOW_MEDIUM_REMOVAL = 0x1e, MMC_READ_10 = 0x28, MMC_READ_12 = 0xa8, MMC_READ_BUFFER = 0x3c, MMC_READ_BUFFER_CAPACITY = 0x5c, MMC_READ_CAPACITY = 0x25, MMC_READ_CD = 0xbe, MMC_READ_CD_MSF = 0xb9, MMC_READ_DISC_INFORMATION = 0x51, MMC_READ_DVD_STRUCTURE = 0xad, MMC_READ_FORMAT_CAPACITIES = 0x23, MMC_READ_SUB_CHANNEL = 0x42, MMC_READ_TOC_PMA_ATIP = 0x43, MMC_READ_TRACK_INFORMATION = 0x52, MMC_REPAIR_TRACK = 0x58, MMC_REPORT_KEY = 0xa4, MMC_REQUEST_SENSE = 0x03, MMC_RESERVE_TRACK = 0x53, MMC_SCAN = 0xba, MMC_SEEK_10 = 0x2b, MMC_SEND_CUE_SHEET = 0x5d, MMC_SEND_DVD_STRUCTURE = 0xbf, MMC_SEND_KEY = 0xa3, MMC_SEND_OPC_INFORMATION = 0x54, MMC_SET_CD_SPEED = 0xbb, MMC_SET_READ_AHEAD = 0xa7, MMC_SET_STREAMING = 0xb6, MMC_START_STOP_UNIT = 0x1b, MMC_STOP_PLAY_SCAN = 0x4e, MMC_SYNCHRONIZE_CACHE = 0x35, MMC_TEST_UNIT_READY = 0x00, MMC_VERIFY_10 = 0x2f, MMC_WRITE_10 = 0xa2, MMC_WRITE_12 = 0xaa, MMC_WRITE_AND_VERIFY_10 = 0x2e, MMC_WRITE_BUFFER = 0x3b, } MMC_OPCODE; typedef enum { SSC_ERASE_6 = 0x19, SSC_FORMAT_MEDIUM = 0x04, SSC_LOAD_UNLOAD = 0x1b, SSC_LOCATE_10 = 0x2b, SSC_LOCATE_16 = 0x92, SSC_MOVE_MEDIUM_ATTACHED = 0xa7, SSC_READ_6 = 0x08, SSC_READ_BLOCK_LIMITS = 0x05, SSC_READ_ELEMENT_STATUS_ATTACHED = 0xb4, SSC_READ_POSITION = 0x34, SSC_READ_REVERSE_6 = 0x0f, SSC_RECOVER_BUFFERED_DATA = 0x14, SSC_REPORT_DENSITY_SUPPORT = 0x44, SSC_REWIND = 0x01, SSC_SET_CAPACITY = 0x0b, SSC_SPACE_6 = 0x11, SSC_SPACE_16 = 0x91, SSC_VERIFY_6 = 0x13, SSC_WRITE_6 = 0x0a, SSC_WRITE_FILEMARKS_6 = 0x10, } SSC_OPCODE; typedef enum { SPC_VPD_DEVICE_IDENTIFICATION = 0x83, SPC_VPD_EXTENDED_INQUIRY_DATA = 0x86, SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES = 0x85, SPC_VPD_MODE_PAGE_POLICY = 0x87, SPC_VPD_SCSI_PORTS = 0x88, SPC_VPD_SOFTWARE_INTERFACE_IDENTIFICATION = 0x84, SPC_VPD_SUPPORTED_VPD_PAGES = 0x00, SPC_VPD_UNIT_SERIAL_NUMBER = 0x80, } SPC_VPD; enum { SPC_PERIPHERAL_DEVICE_TYPE_DISK = 0x00, SPC_PERIPHERAL_DEVICE_TYPE_TAPE = 0x01, SPC_PERIPHERAL_DEVICE_TYPE_DVD = 0x05, SPC_PERIPHERAL_DEVICE_TYPE_CHANGER = 0x08, SPC_VERSION_NONE = 0x00, SPC_VERSION_SPC = 0x03, SPC_VERSION_SPC2 = 0x04, SPC_VERSION_SPC3 = 0x05, SPC_VERSION_SPC4 = 0x06, SPC_PROTOCOL_IDENTIFIER_FC = 0x00, SPC_PROTOCOL_IDENTIFIER_PSCSI = 0x01, SPC_PROTOCOL_IDENTIFIER_SSA = 0x02, SPC_PROTOCOL_IDENTIFIER_IEEE1394 = 0x03, SPC_PROTOCOL_IDENTIFIER_RDMA = 0x04, SPC_PROTOCOL_IDENTIFIER_ISCSI = 0x05, SPC_PROTOCOL_IDENTIFIER_SAS = 0x06, SPC_PROTOCOL_IDENTIFIER_ADT = 0x07, SPC_PROTOCOL_IDENTIFIER_ATA = 0x08, SPC_VPD_CODE_SET_BINARY = 0x01, SPC_VPD_CODE_SET_ASCII = 0x02, SPC_VPD_CODE_SET_UTF8 = 0x03, SPC_VPD_ASSOCIATION_LOGICAL_UNIT = 0x00, SPC_VPD_ASSOCIATION_TARGET_PORT = 0x01, SPC_VPD_ASSOCIATION_TARGET_DEVICE = 0x02, SPC_VPD_IDENTIFIER_TYPE_VENDOR_SPECIFIC = 0x00, SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID = 0x01, SPC_VPD_IDENTIFIER_TYPE_EUI64 = 0x02, SPC_VPD_IDENTIFIER_TYPE_NAA = 0x03, SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT = 0x04, SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP = 0x05, SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP = 0x06, SPC_VPD_IDENTIFIER_TYPE_MD5_LOGICAL_UNIT = 0x07, SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME = 0x08, }; #endif /* ISTGT_SCSI_H */ istgt-20111008/src/istgt_iscsi.c000644 000000 000000 00000506116 11643630071 016472 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_PTHREAD_NP_H #include #endif #include #include #include #include #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_crc32c.h" #include "istgt_md5.h" #include "istgt_iscsi.h" #include "istgt_iscsi_param.h" #include "istgt_lu.h" #include "istgt_proto.h" #include "istgt_scsi.h" #include "istgt_queue.h" #ifdef ISTGT_USE_KQUEUE #include #include #include #endif /* according to RFC1982 */ #define SN32_CMPMAX (((uint32_t)1U) << (32 - 1)) #define SN32_LT(S1,S2) \ (((uint32_t)(S1) != (uint32_t)(S2)) \ && (((uint32_t)(S1) < (uint32_t)(S2) \ && ((uint32_t)(S2) - (uint32_t)(S1) < SN32_CMPMAX)) \ || ((uint32_t)(S1) > (uint32_t)(S2) \ && ((uint32_t)(S1) - (uint32_t)(S2) > SN32_CMPMAX)))) #define SN32_GT(S1,S2) \ (((uint32_t)(S1) != (uint32_t)(S2)) \ && (((uint32_t)(S1) < (uint32_t)(S2) \ && ((uint32_t)(S2) - (uint32_t)(S1) > SN32_CMPMAX)) \ || ((uint32_t)(S1) > (uint32_t)(S2) \ && ((uint32_t)(S1) - (uint32_t)(S2) < SN32_CMPMAX)))) #define POLLWAIT 3000 #define MAX_MCSREVWAIT (10 * 1000) #define ISCMDQ 8 #define ISCSI_GETVAL(PARAMS,KEY) \ istgt_iscsi_param_get_val((PARAMS),(KEY)) #define ISCSI_EQVAL(PARAMS,KEY,VAL) \ istgt_iscsi_param_eq_val((PARAMS),(KEY),(VAL)) #define ISCSI_DELVAL(PARAMS,KEY) \ istgt_iscsi_param_del((PARAMS),(KEY)) #define ISCSI_ADDVAL(PARAMS,KEY,VAL,LIST,TYPE) \ istgt_iscsi_param_add((PARAMS),(KEY),(VAL), (LIST), (TYPE)) static int g_nconns; static CONN_Ptr *g_conns; static pthread_mutex_t g_conns_mutex; static uint16_t g_last_tsih; static pthread_mutex_t g_last_tsih_mutex; static int istgt_add_transfer_task(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); static void istgt_clear_transfer_task(CONN_Ptr conn, uint32_t CmdSN); static void istgt_clear_all_transfer_task(CONN_Ptr conn); static int istgt_iscsi_send_r2t(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, int offset, int len, uint32_t transfer_tag, uint32_t *R2TSN); static int istgt_append_sess(CONN_Ptr conn, uint64_t isid, uint16_t tsih, uint16_t cid); static void istgt_remove_conn(CONN_Ptr conn); static int istgt_iscsi_drop_all_conns(CONN_Ptr conn); static int istgt_iscsi_drop_old_conns(CONN_Ptr conn); #if 0 #define ISTGT_USE_RECVBLOCK #define ISTGT_USE_SENDBLOCK #endif #if 0 #define ISTGT_USE_RECVWAIT #endif static ssize_t istgt_iscsi_read(CONN_Ptr conn, void *buf, size_t nbytes) { #ifndef ISTGT_USE_RECVBLOCK uint8_t padding[ISCSI_ALIGNMENT]; #endif uint8_t *cp; size_t pad_bytes; size_t total; ssize_t r; total = 0; cp = (uint8_t *) buf; #ifdef ISTGT_USE_RECVBLOCK pad_bytes = ISCSI_ALIGN(nbytes) - nbytes; do { #ifdef ISTGT_USE_RECVWAIT r = recv(conn->sock, cp + total, (nbytes + pad_bytes - total), MSG_WAITALL); #else r = recv(conn->sock, cp + total, (nbytes + pad_bytes - total), 0); #endif if (r < 0) { /* error */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read error (errno=%d)\n", errno); return r; } if (r == 0) { /* EOF */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read EOF\n"); return r; } total += r; } while (total < nbytes); if (total != (nbytes + pad_bytes)) { /* incomplete bytes */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d/%d+%d bytes\n", total, nbytes, pad_bytes); if (total > nbytes) { total = nbytes; } return total; } if (pad_bytes != 0) { /* complete padding */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d bytes (padding %d)\n", nbytes, pad_bytes); } else { /* just aligned */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d bytes (no padding)\n", nbytes); } #else /* !ISTGT_USE_RECVBLOCK */ do { r = istgt_read_socket(conn->sock, cp + total, (nbytes - total), conn->timeout); if (r < 0) { /* error */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read error (errno=%d)\n", errno); return r; } if (r == 0) { /* EOF */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read EOF\n"); return r; } total += r; } while (total < nbytes); #if 0 ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "RAW DATA", cp, total); #endif if (total != nbytes) { /* incomplete bytes */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d/%d bytes\n", total, nbytes); return total; } /* need padding? */ pad_bytes = ISCSI_ALIGN(nbytes) - nbytes; if (pad_bytes != 0) { total = 0; cp = (uint8_t *) &padding[0]; do { r = istgt_read_socket(conn->sock, cp + total, (pad_bytes - total), conn->timeout); if (r < 0) { /* error */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d bytes (padding error) (errno=%d)\n", nbytes, errno); return nbytes; } if (r == 0) { /* EOF */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d bytes (padding EOF)\n", nbytes); return nbytes; } total += r; } while (total < pad_bytes); if (total != pad_bytes) { /* incomplete padding */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d bytes (padding %d)\n", nbytes, total); return nbytes; } /* complete padding */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d bytes (padding %d)\n", nbytes, pad_bytes); return nbytes; } /* just aligned */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Read %d bytes (no padding)\n", nbytes); #endif /* ISTGT_USE_RECVBLOCK */ return nbytes; } static ssize_t istgt_iscsi_write(CONN_Ptr conn, const void *buf, size_t nbytes) { uint8_t padding[ISCSI_ALIGNMENT]; const uint8_t *cp; size_t pad_bytes; size_t total; ssize_t r; total = 0; cp = (const uint8_t *) buf; #ifdef ISTGT_USE_SENDBLOCK pad_bytes = ISCSI_ALIGN(nbytes) - nbytes; do { r = send(conn->wsock, cp, nbytes, 0); if (r < 0) { /* error */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write error (errno=%d)\n", errno); return r; } total += r; } while (total < nbytes); if (total != nbytes) { /* incomplete bytes */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d/%d bytes\n", total, nbytes); return total; } if (pad_bytes != 0) { memset(padding, 0, sizeof padding); total = 0; cp = (const uint8_t *) &padding[0]; do { r = send(conn->wsock, cp, pad_bytes, 0); if (r < 0) { /* error */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (padding error) (errno=%d)\n", nbytes, errno); return nbytes; } total += r; } while (total < pad_bytes); if (total != pad_bytes) { /* incomplete padding */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (padding %d)\n", nbytes, total); return nbytes; } /* complete padding */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (padding %d)\n", nbytes, pad_bytes); } else { /* just aligned */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (no padding)\n", nbytes); } #else /* !ISTGT_USE_SENDBLOCK */ do { r = istgt_write_socket(conn->wsock, cp + total, (nbytes - total), conn->timeout); if (r < 0) { /* error */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write error (errno=%d)\n", errno); return r; } total += r; } while (total < nbytes); if (total != nbytes) { /* incomplete bytes */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d/%d bytes\n", total, nbytes); return r; } /* need padding? */ pad_bytes = ISCSI_ALIGN(nbytes) - nbytes; if (pad_bytes != 0) { memset(padding, 0, sizeof padding); total = 0; cp = (const uint8_t *) &padding[0]; do { r = istgt_write_socket(conn->wsock, cp + total, (pad_bytes - total), conn->timeout); if (r < 0) { /* error */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (padding error) (errno=%d)\n", nbytes, errno); return nbytes; } total += r; } while (total < pad_bytes); if (total != pad_bytes) { /* incomplete padding */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (padding %d)\n", nbytes, total); return nbytes; } /* complete padding */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (padding %d)\n", nbytes, pad_bytes); return nbytes; } /* just aligned */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "Write %d bytes (no padding)\n", nbytes); #endif /* ISTGT_USE_SENDBLOCK */ return nbytes; } #define MATCH_DIGEST_WORD(BUF, CRC32C) \ ( ((((uint32_t) *((uint8_t *)(BUF)+0)) << 0) \ | (((uint32_t) *((uint8_t *)(BUF)+1)) << 8) \ | (((uint32_t) *((uint8_t *)(BUF)+2)) << 16) \ | (((uint32_t) *((uint8_t *)(BUF)+3)) << 24)) \ == (CRC32C)) #define MAKE_DIGEST_WORD(BUF, CRC32C) \ ( ((*((uint8_t *)(BUF)+0)) = (uint8_t)((uint32_t)(CRC32C) >> 0)), \ ((*((uint8_t *)(BUF)+1)) = (uint8_t)((uint32_t)(CRC32C) >> 8)), \ ((*((uint8_t *)(BUF)+2)) = (uint8_t)((uint32_t)(CRC32C) >> 16)), \ ((*((uint8_t *)(BUF)+3)) = (uint8_t)((uint32_t)(CRC32C) >> 24))) #if 0 static int istgt_match_digest_word(const uint8_t *buf, uint32_t crc32c) { uint32_t l; l = (buf[0] & 0xffU) << 0; l |= (buf[1] & 0xffU) << 8; l |= (buf[2] & 0xffU) << 16; l |= (buf[3] & 0xffU) << 24; return (l == crc32c); } static uint8_t * istgt_make_digest_word(uint8_t *buf, size_t len, uint32_t crc32c) { if (len < ISCSI_DIGEST_LEN) return NULL; buf[0] = (crc32c >> 0) & 0xffU; buf[1] = (crc32c >> 8) & 0xffU; buf[2] = (crc32c >> 16) & 0xffU; buf[3] = (crc32c >> 24) & 0xffU; return buf; } #endif static int istgt_iscsi_read_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { uint32_t crc32c; int total_ahs_len; int data_len; int segment_len; int total; int rc; pdu->ahs = NULL; pdu->total_ahs_len = 0; pdu->data = NULL; pdu->data_segment_len = 0; total = 0; /* BHS */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "BHS read %d\n", ISCSI_BHS_LEN); rc = istgt_iscsi_read(conn, &pdu->bhs, ISCSI_BHS_LEN); if (rc < 0) { if (errno == ECONNRESET) { ISTGT_WARNLOG("Connection reset by peer (%s)\n", conn->initiator_name); conn->state = CONN_STATE_EXITING; } else if (errno == ETIMEDOUT) { ISTGT_WARNLOG("Operation timed out (%s)\n", conn->initiator_name); conn->state = CONN_STATE_EXITING; } else { ISTGT_ERRLOG("iscsi_read() failed (errno=%d)\n", errno); } return -1; } if (rc == 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "iscsi_read() EOF\n"); conn->state = CONN_STATE_EXITING; return -1; } if (rc != ISCSI_BHS_LEN) { ISTGT_ERRLOG("invalid BHS length (%d)\n", rc); return -1; } total += ISCSI_BHS_LEN; /* AHS */ total_ahs_len = DGET8(&pdu->bhs.total_ahs_len); if (total_ahs_len != 0) { pdu->ahs = xmalloc(ISCSI_ALIGN((4 * total_ahs_len))); ISTGT_TRACELOG(ISTGT_TRACE_NET, "AHS read %d\n", (4 * total_ahs_len)); rc = istgt_iscsi_read(conn, pdu->ahs, (4 * total_ahs_len)); if (rc < 0) { ISTGT_ERRLOG("iscsi_read() failed\n"); return -1; } if (rc == 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "iscsi_read() EOF\n"); conn->state = CONN_STATE_EXITING; return -1; } if (rc != (4 * total_ahs_len)) { ISTGT_ERRLOG("invalid AHS length (%d)\n", rc); return -1; } pdu->total_ahs_len = total_ahs_len; total += (4 * total_ahs_len); } else { pdu->ahs = NULL; pdu->total_ahs_len = 0; } /* Header Digest */ if (conn->header_digest) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "HeaderDigest read %d\n", ISCSI_DIGEST_LEN); rc = istgt_iscsi_read(conn, pdu->header_digest, ISCSI_DIGEST_LEN); if (rc < 0) { ISTGT_ERRLOG("iscsi_read() failed\n"); { int opcode = BGET8W(&pdu->bhs.opcode, 5, 6); ISTGT_ERRLOG("Header Digest read error (opcode = 0x%x)\n", opcode); } return -1; } if (rc == 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "iscsi_read() EOF\n"); conn->state = CONN_STATE_EXITING; return -1; } if (rc != ISCSI_DIGEST_LEN) { ISTGT_ERRLOG("invalid Header Digest length (%d)\n", rc); return -1; } total += ISCSI_DIGEST_LEN; } /* Data Segment */ data_len = DGET24(&pdu->bhs.data_segment_len[0]); if (data_len != 0) { if (conn->sess == NULL) { segment_len = DEFAULT_FIRSTBURSTLENGTH; } else { segment_len = conn->MaxRecvDataSegmentLength; } if (data_len > segment_len) { ISTGT_ERRLOG("Data(%d) > Segment(%d)\n", data_len, segment_len); return -1; } if (ISCSI_ALIGN(data_len) <= ISTGT_SHORTDATASIZE) { pdu->data = pdu->shortdata; } else { pdu->data = xmalloc(ISCSI_ALIGN(segment_len)); } ISTGT_TRACELOG(ISTGT_TRACE_NET, "Data read %d\n", data_len); rc = istgt_iscsi_read(conn, pdu->data, data_len); if (rc < 0) { ISTGT_ERRLOG("iscsi_read() failed (%d,errno=%d)\n", rc, errno); return -1; } if (rc == 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "iscsi_read() EOF\n"); conn->state = CONN_STATE_EXITING; return -1; } if (rc != data_len) { ISTGT_ERRLOG("invalid Data Segment length (%d)\n", rc); return -1; } pdu->data_segment_len = data_len; total += data_len; #if 0 if (data_len > 512) { ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "DataSegment", pdu->data, 512); } else { ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "DataSegment", pdu->data, data_len); } #endif } else { pdu->data = NULL; pdu->data_segment_len = 0; } /* Data Digest */ if (conn->data_digest && data_len != 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "DataDigest read %d\n", ISCSI_DIGEST_LEN); rc = istgt_iscsi_read(conn, pdu->data_digest, ISCSI_DIGEST_LEN); if (rc < 0) { ISTGT_ERRLOG("iscsi_read() failed\n"); return -1; } if (rc == 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "iscsi_read() EOF\n"); conn->state = CONN_STATE_EXITING; return -1; } if (rc != ISCSI_DIGEST_LEN) { ISTGT_ERRLOG("invalid Data Digest length (%d)\n", rc); return -1; } total += ISCSI_DIGEST_LEN; } /* check digest */ if (conn->header_digest) { if (total_ahs_len == 0) { crc32c = istgt_crc32c((uint8_t *) &pdu->bhs, ISCSI_BHS_LEN); } else { int upd_total = 0; crc32c = ISTGT_CRC32C_INITIAL; crc32c = istgt_update_crc32c((uint8_t *) &pdu->bhs, ISCSI_BHS_LEN, crc32c); upd_total += ISCSI_BHS_LEN; crc32c = istgt_update_crc32c((uint8_t *) pdu->ahs, (4 * total_ahs_len), crc32c); upd_total += (4 * total_ahs_len); crc32c = istgt_fixup_crc32c(upd_total, crc32c); crc32c = crc32c ^ ISTGT_CRC32C_XOR; } rc = MATCH_DIGEST_WORD(pdu->header_digest, crc32c); if (rc == 0) { ISTGT_ERRLOG("header digest error\n"); return -1; } } if (conn->data_digest && data_len != 0) { crc32c = istgt_crc32c(pdu->data, data_len); rc = MATCH_DIGEST_WORD(pdu->data_digest, crc32c); if (rc == 0) { ISTGT_ERRLOG("data digest error\n"); return -1; } } return total; } static int istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PDU_Ptr pdu); static int istgt_iscsi_write_pdu(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { int rc; if (conn->use_sender == 0) { rc = istgt_iscsi_write_pdu_internal(conn, pdu); } else { ISTGT_LU_TASK_Ptr lu_task; ISCSI_PDU_Ptr src_pdu, dst_pdu; uint8_t *cp; int total_ahs_len; int data_len; int alloc_len; int total; cp = (uint8_t *) &pdu->bhs; total_ahs_len = DGET8(&cp[4]); data_len = DGET24(&cp[5]); total = 0; #if 0 ISTGT_LOG("W:PDU OP=%x, tag=%x, ExpCmdSN=%u, MaxCmdSN=%u\n", DGET8(&cp[0]), DGET32(&cp[32]), DGET32(&cp[28]), DGET32(&cp[32])); #endif /* allocate for queued PDU */ alloc_len = ISCSI_ALIGN(sizeof *lu_task); alloc_len += ISCSI_ALIGN(sizeof *lu_task->lu_cmd.pdu); alloc_len += ISCSI_ALIGN(4 * total_ahs_len); alloc_len += ISCSI_ALIGN(data_len); lu_task = xmalloc(alloc_len); memset(lu_task, 0, alloc_len); lu_task->lu_cmd.pdu = (ISCSI_PDU_Ptr) ((uint8_t *)lu_task + ISCSI_ALIGN(sizeof *lu_task)); lu_task->lu_cmd.pdu->ahs = (ISCSI_AHS *) ((uint8_t *)lu_task->lu_cmd.pdu + ISCSI_ALIGN(sizeof *lu_task->lu_cmd.pdu)); lu_task->lu_cmd.pdu->data = (uint8_t *)lu_task->lu_cmd.pdu->ahs + ISCSI_ALIGN(4 * total_ahs_len); /* specify type and self conn */ lu_task->type = ISTGT_LU_TASK_REQPDU; lu_task->conn = conn; /* copy PDU structure */ src_pdu = pdu; dst_pdu = lu_task->lu_cmd.pdu; memcpy(&dst_pdu->bhs, &src_pdu->bhs, ISCSI_BHS_LEN); total += ISCSI_BHS_LEN; if (total_ahs_len != 0) { memcpy(dst_pdu->ahs, src_pdu->ahs, 4 * total_ahs_len); total += (4 * total_ahs_len); } else { dst_pdu->ahs = NULL; } if (conn->header_digest) { memcpy(dst_pdu->header_digest, src_pdu->header_digest, ISCSI_DIGEST_LEN); total += ISCSI_DIGEST_LEN; } if (data_len != 0) { memcpy(dst_pdu->data, src_pdu->data, data_len); total += data_len; } else { dst_pdu->data = NULL; } if (conn->data_digest && data_len != 0) { memcpy(dst_pdu->data_digest, src_pdu->data_digest, ISCSI_DIGEST_LEN); total += ISCSI_DIGEST_LEN; } /* insert to queue */ MTX_LOCK(&conn->result_queue_mutex); rc = istgt_queue_enqueue(&conn->result_queue, lu_task); if (rc != 0) { MTX_UNLOCK(&conn->result_queue_mutex); ISTGT_ERRLOG("queue_enqueue() failed\n"); return -1; } /* notify to thread */ rc = pthread_cond_broadcast(&conn->result_queue_cond); MTX_UNLOCK(&conn->result_queue_mutex); if (rc != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); return -1; } /* total bytes should be sent in queue */ rc = total; } return rc; } static int istgt_iscsi_write_pdu_internal(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { uint8_t *cp; uint32_t crc32c; int enable_digest; int opcode; int total_ahs_len; int data_len; int total; int rc; cp = (uint8_t *) &pdu->bhs; total_ahs_len = DGET8(&cp[4]); data_len = DGET24(&cp[5]); total = 0; enable_digest = 1; opcode = BGET8W(&cp[0], 5, 6); if (opcode == ISCSI_OP_LOGIN_RSP) { /* this PDU should be sent without digest */ enable_digest = 0; } #define ISTGT_USE_SHORTPDU_WRITE #ifdef ISTGT_USE_SHORTPDU_WRITE /* if short size, BHS + AHS + HD + DATA + DD */ if (total_ahs_len == 0 && data_len <= ISTGT_SHORTDATASIZE) { uint8_t *spp = conn->shortpdu; int pad_len = 0; memcpy(spp, (uint8_t *) &pdu->bhs, ISCSI_BHS_LEN); total = ISCSI_BHS_LEN; if (enable_digest && conn->header_digest) { crc32c = istgt_crc32c(spp, total); MAKE_DIGEST_WORD(spp + total, crc32c); total += ISCSI_DIGEST_LEN; } memcpy(spp + total, pdu->data, data_len); total += data_len; if ((data_len % ISCSI_ALIGNMENT) != 0) { memset(spp + total, 0, ISCSI_ALIGN(data_len) - data_len); total += ISCSI_ALIGN(data_len) - data_len; pad_len += ISCSI_ALIGN(data_len) - data_len; } if (enable_digest && conn->data_digest && data_len != 0) { crc32c = istgt_crc32c(pdu->data, data_len); MAKE_DIGEST_WORD(spp + total, crc32c); total += ISCSI_DIGEST_LEN; } ISTGT_TRACELOG(ISTGT_TRACE_NET, "PDU write %d\n", total); rc = istgt_iscsi_write(conn, spp, total); if (rc < 0) { ISTGT_ERRLOG("iscsi_write() failed (errno=%d)\n", errno); return -1; } if (rc != total) { ISTGT_ERRLOG("incomplete PDU length (%d)\n", rc); return -1; } return total - pad_len; } #endif /* ISTGT_USE_SHORTPDU_WRITE */ /* BHS */ ISTGT_TRACELOG(ISTGT_TRACE_NET, "BHS write %d\n", ISCSI_BHS_LEN); rc = istgt_iscsi_write(conn, &pdu->bhs, ISCSI_BHS_LEN); if (rc < 0) { ISTGT_ERRLOG("iscsi_write() failed (errno=%d)\n", errno); return -1; } if (rc != ISCSI_BHS_LEN) { ISTGT_ERRLOG("incomplete BHS length (%d)\n", rc); return -1; } total += ISCSI_BHS_LEN; /* AHS */ if (total_ahs_len != 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "AHS write %d\n", (4 * total_ahs_len)); rc = istgt_iscsi_write(conn, pdu->ahs, (4 * total_ahs_len)); if (rc < 0) { ISTGT_ERRLOG("iscsi_write() failed\n"); return -1; } if (rc != (4 * total_ahs_len)) { ISTGT_ERRLOG("incomplete AHS length (%d)\n", rc); return -1; } total += (4 * total_ahs_len); } /* Header Digest */ if (enable_digest && conn->header_digest) { if (total_ahs_len == 0) { crc32c = istgt_crc32c((uint8_t *) &pdu->bhs, ISCSI_BHS_LEN); } else { int upd_total = 0; crc32c = ISTGT_CRC32C_INITIAL; crc32c = istgt_update_crc32c((uint8_t *) &pdu->bhs, ISCSI_BHS_LEN, crc32c); upd_total += ISCSI_BHS_LEN; crc32c = istgt_update_crc32c((uint8_t *) pdu->ahs, (4 * total_ahs_len), crc32c); upd_total += (4 * total_ahs_len); crc32c = istgt_fixup_crc32c(upd_total, crc32c); crc32c = crc32c ^ ISTGT_CRC32C_XOR; } MAKE_DIGEST_WORD(pdu->header_digest, crc32c); ISTGT_TRACELOG(ISTGT_TRACE_NET, "HeaderDigest write %d\n", ISCSI_DIGEST_LEN); rc = istgt_iscsi_write(conn, pdu->header_digest, ISCSI_DIGEST_LEN); if (rc < 0) { ISTGT_ERRLOG("iscsi_write() failed\n"); return -1; } if (rc != ISCSI_DIGEST_LEN) { ISTGT_ERRLOG("incomplete Header Digest length (%d)\n", rc); return -1; } total += ISCSI_DIGEST_LEN; } /* Data Segment */ if (data_len != 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "Data write %d\n", data_len); rc = istgt_iscsi_write(conn, pdu->data, data_len); if (rc < 0) { ISTGT_ERRLOG("iscsi_write() failed\n"); return -1; } if (rc != data_len) { ISTGT_ERRLOG("incomplete Data Segment length (%d)\n", rc); return -1; } total += data_len; } /* Data Digest */ if (enable_digest && conn->data_digest && data_len != 0) { crc32c = istgt_crc32c(pdu->data, data_len); MAKE_DIGEST_WORD(pdu->data_digest, crc32c); ISTGT_TRACELOG(ISTGT_TRACE_NET, "DataDigest write %d\n", ISCSI_DIGEST_LEN); ISTGT_TRACELOG(ISTGT_TRACE_NET, "DataDigest %x\n", crc32c); rc = istgt_iscsi_write(conn, pdu->data_digest, ISCSI_DIGEST_LEN); if (rc < 0) { ISTGT_ERRLOG("iscsi_write() failed\n"); return -1; } if (rc != ISCSI_DIGEST_LEN) { ISTGT_ERRLOG("incomplete Data Digest length (%d)\n", rc); return -1; } total += ISCSI_DIGEST_LEN; } return total; } int istgt_iscsi_copy_pdu(ISCSI_PDU_Ptr dst_pdu, ISCSI_PDU_Ptr src_pdu) { memcpy(&dst_pdu->bhs, &src_pdu->bhs, ISCSI_BHS_LEN); dst_pdu->ahs = src_pdu->ahs; memcpy(dst_pdu->header_digest, src_pdu->header_digest, ISCSI_DIGEST_LEN); if (src_pdu->data == src_pdu->shortdata) { memcpy(dst_pdu->shortdata, src_pdu->shortdata, sizeof src_pdu->shortdata); dst_pdu->data = dst_pdu->shortdata; } else { dst_pdu->data = src_pdu->data; } memcpy(dst_pdu->data_digest, src_pdu->data_digest, ISCSI_DIGEST_LEN); dst_pdu->total_ahs_len = src_pdu->total_ahs_len; dst_pdu->data_segment_len = src_pdu->data_segment_len; dst_pdu->copy_pdu = 0; src_pdu->copy_pdu = 1; return 0; } typedef struct iscsi_param_table_t { const char *key; const char *val; const char *list; int type; } ISCSI_PARAM_TABLE; static ISCSI_PARAM_TABLE conn_param_table[] = { { "HeaderDigest", "None", "CRC32C,None", ISPT_LIST }, { "DataDigest", "None", "CRC32C,None", ISPT_LIST }, { "MaxRecvDataSegmentLength", "8192", "512,16777215", ISPT_NUMERICAL }, { "OFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND }, { "IFMarker", "No", "Yes,No", ISPT_BOOLEAN_AND }, { "OFMarkInt", "1", "1,65535", ISPT_NUMERICAL }, { "IFMarkInt", "1", "1,65535", ISPT_NUMERICAL }, { "AuthMethod", "None", "CHAP,None", ISPT_LIST }, { "CHAP_A", "5", "5", ISPT_LIST }, { "CHAP_N", "", "", ISPT_DECLARATIVE }, { "CHAP_R", "", "", ISPT_DECLARATIVE }, { "CHAP_I", "", "", ISPT_DECLARATIVE }, { "CHAP_C", "", "", ISPT_DECLARATIVE }, { NULL, NULL, NULL, ISPT_INVALID }, }; static ISCSI_PARAM_TABLE sess_param_table[] = { { "MaxConnections", "1", "1,65535", ISPT_NUMERICAL }, #if 0 /* need special handling */ { "SendTargets", "", "", ISPT_DECLARATIVE }, #endif { "TargetName", "", "", ISPT_DECLARATIVE }, { "InitiatorName", "", "", ISPT_DECLARATIVE }, { "TargetAlias", "", "", ISPT_DECLARATIVE }, { "InitiatorAlias", "", "", ISPT_DECLARATIVE }, { "TargetAddress", "", "", ISPT_DECLARATIVE }, { "TargetPortalGroupTag", "1", "1,65535", ISPT_NUMERICAL }, { "InitialR2T", "Yes", "Yes,No", ISPT_BOOLEAN_OR }, { "ImmediateData", "Yes", "Yes,No", ISPT_BOOLEAN_AND }, { "MaxBurstLength", "262144", "512,16777215", ISPT_NUMERICAL }, { "FirstBurstLength", "65536", "512,16777215", ISPT_NUMERICAL }, { "DefaultTime2Wait", "2", "0,3600", ISPT_NUMERICAL_MAX }, { "DefaultTime2Retain", "20", "0,3600", ISPT_NUMERICAL }, { "MaxOutstandingR2T", "1", "1,65536", ISPT_NUMERICAL }, { "DataPDUInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR }, { "DataSequenceInOrder", "Yes", "Yes,No", ISPT_BOOLEAN_OR }, { "ErrorRecoveryLevel", "0", "0,2", ISPT_NUMERICAL }, { "SessionType", "Normal", "Normal,Discovery", ISPT_DECLARATIVE }, { NULL, NULL, NULL, ISPT_INVALID }, }; static int istgt_iscsi_params_init_internal(ISCSI_PARAM **params, ISCSI_PARAM_TABLE *table) { int rc; int i; for (i = 0; table[i].key != NULL; i++) { rc = istgt_iscsi_param_add(params, table[i].key, table[i].val, table[i].list, table[i].type); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_add() failed\n"); return -1; } } return 0; } static int istgt_iscsi_conn_params_init(ISCSI_PARAM **params) { return istgt_iscsi_params_init_internal(params, &conn_param_table[0]); } static int istgt_iscsi_sess_params_init(ISCSI_PARAM **params) { return istgt_iscsi_params_init_internal(params, &sess_param_table[0]); } static char * istgt_iscsi_param_get_val(ISCSI_PARAM *params, const char *key) { ISCSI_PARAM *param; param = istgt_iscsi_param_find(params, key); if (param == NULL) return NULL; return param->val; } static int istgt_iscsi_param_eq_val(ISCSI_PARAM *params, const char *key, const char *val) { ISCSI_PARAM *param; param = istgt_iscsi_param_find(params, key); if (param == NULL) return 0; if (strcasecmp(param->val, val) == 0) return 1; return 0; } #if 0 static int istgt_iscsi_print_params(ISCSI_PARAM *params) { ISCSI_PARAM *param; for (param = params; param != NULL; param = param->next) { printf("key=[%s] val=[%s] list=[%s] type=%d\n", param->key, param->val, param->list, param->type); } return 0; } #endif static int istgt_iscsi_negotiate_params(CONN_Ptr conn, ISCSI_PARAM *params, uint8_t *data, int alloc_len, int data_len) { ISCSI_PARAM *param; ISCSI_PARAM *cur_param; char *valid_list, *in_val; char *valid_next, *in_next; char *cur_val; char *new_val; char *valid_val; char *min_val, *max_val; int discovery; int cur_type; int val_i, cur_val_i; int min_i, max_i; int total; int len; int sw; total = data_len; if (alloc_len < 1) { return 0; } if (total > alloc_len) { total = alloc_len; data[total - 1] = '\0'; return total; } if (params == NULL) { /* no input */ return total; } /* discovery? */ discovery = 0; cur_param = istgt_iscsi_param_find(params, "SessionType"); if (cur_param == NULL) { SESS_MTX_LOCK(conn); cur_param = istgt_iscsi_param_find(conn->sess->params, "SessionType"); if (cur_param == NULL) { /* no session type */ } else { if (strcasecmp(cur_param->val, "Discovery") == 0) { discovery = 1; } } SESS_MTX_UNLOCK(conn); } else { if (strcasecmp(cur_param->val, "Discovery") == 0) { discovery = 1; } } /* for temporary store */ valid_list = xmalloc(ISCSI_TEXT_MAX_VAL_LEN + 1); in_val = xmalloc(ISCSI_TEXT_MAX_VAL_LEN + 1); cur_val = xmalloc(ISCSI_TEXT_MAX_VAL_LEN + 1); for (param = params; param != NULL; param = param->next) { /* sendtargets is special */ if (strcasecmp(param->key, "SendTargets") == 0) { continue; } /* CHAP keys */ if (strcasecmp(param->key, "CHAP_A") == 0 || strcasecmp(param->key, "CHAP_N") == 0 || strcasecmp(param->key, "CHAP_R") == 0 || strcasecmp(param->key, "CHAP_I") == 0 || strcasecmp(param->key, "CHAP_C") == 0) { continue; } if (discovery) { /* 12.2, 12.10, 12.11, 12.13, 12.14, 12.17, 12.18, 12.19 */ if (strcasecmp(param->key, "MaxConnections") == 0 || strcasecmp(param->key, "InitialR2T") == 0 || strcasecmp(param->key, "ImmediateData") == 0 || strcasecmp(param->key, "MaxBurstLength") == 0 || strcasecmp(param->key, "FirstBurstLength") == 0 || strcasecmp(param->key, "MaxOutstandingR2T") == 0 || strcasecmp(param->key, "DataPDUInOrder") == 0 || strcasecmp(param->key, "DataSequenceInOrder") == 0) { strlcpy(in_val, "Irrelevant", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; cur_type = -1; goto add_val; } } /* get current param */ sw = 0; cur_param = istgt_iscsi_param_find(conn->params, param->key); if (cur_param == NULL) { sw = 1; SESS_MTX_LOCK(conn); cur_param = istgt_iscsi_param_find(conn->sess->params, param->key); if (cur_param == NULL) { SESS_MTX_UNLOCK(conn); if (strncasecmp(param->key, "X-", 2) == 0 || strncasecmp(param->key, "X#", 2) == 0) { /* Extension Key */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "extension key %.64s\n", param->key); } else { ISTGT_ERRLOG("unknown key %.64s\n", param->key); } strlcpy(in_val, "NotUnderstood", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; cur_type = -1; goto add_val; } strlcpy(valid_list, cur_param->list, ISCSI_TEXT_MAX_VAL_LEN); strlcpy(cur_val, cur_param->val, ISCSI_TEXT_MAX_VAL_LEN); cur_type = cur_param->type; SESS_MTX_UNLOCK(conn); } else { strlcpy(valid_list, cur_param->list, ISCSI_TEXT_MAX_VAL_LEN); strlcpy(cur_val, cur_param->val, ISCSI_TEXT_MAX_VAL_LEN); cur_type = cur_param->type; } /* negotiate value */ switch (cur_type) { case ISPT_LIST: strlcpy(in_val, param->val, ISCSI_TEXT_MAX_VAL_LEN); in_next = in_val; while ((new_val = strsepq(&in_next, ",")) != NULL) { valid_next = valid_list; while ((valid_val = strsepq(&valid_next, ",")) != NULL) { if (strcasecmp(new_val, valid_val) == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "match %s\n", new_val); goto update_val; } } } if (new_val == NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "key %.64s reject\n", param->key); strlcpy(in_val, "Reject", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; goto add_val; } break; case ISPT_NUMERICAL: val_i = (int) strtol(param->val, NULL, 10); cur_val_i = (int) strtol(cur_val, NULL, 10); valid_next = valid_list; min_val = strsepq(&valid_next, ","); max_val = strsepq(&valid_next, ","); if (min_val != NULL) { min_i = (int) strtol(min_val, NULL, 10); } else { min_i = 0; } if (max_val != NULL) { max_i = (int) strtol(max_val, NULL, 10); } else { max_i = 0; } if (val_i < min_i || val_i > max_i) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "key %.64s reject\n", param->key); strlcpy(in_val, "Reject", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; goto add_val; } if (strcasecmp(param->key, "MaxRecvDataSegmentLength") == 0) { /* Declarative, but set as same value */ cur_val_i = conn->TargetMaxRecvDataSegmentLength; } if (val_i > cur_val_i) { val_i = cur_val_i; } snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d", val_i); new_val = in_val; break; case ISPT_NUMERICAL_MAX: val_i = (int) strtol(param->val, NULL, 10); cur_val_i = (int) strtol(cur_val, NULL, 10); valid_next = valid_list; min_val = strsepq(&valid_next, ","); max_val = strsepq(&valid_next, ","); if (min_val != NULL) { min_i = (int) strtol(min_val, NULL, 10); } else { min_i = 0; } if (max_val != NULL) { max_i = (int) strtol(max_val, NULL, 10); } else { max_i = 0; } if (val_i < min_i || val_i > max_i) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "key %.64s reject\n", param->key); strlcpy(in_val, "Reject", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; goto add_val; } if (val_i < cur_val_i) { val_i = cur_val_i; } snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d", val_i); new_val = in_val; break; case ISPT_BOOLEAN_OR: if (strcasecmp(cur_val, "Yes") == 0) { /* YES || XXX */ strlcpy(in_val, "Yes", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; } else { if (strcasecmp(param->val, "Yes") == 0 || strcasecmp(param->val, "No") == 0) { new_val = param->val; } else { /* unknown value */ strlcpy(in_val, "Reject", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; goto add_val; } } break; case ISPT_BOOLEAN_AND: if (strcasecmp(cur_val, "No") == 0) { /* No && XXX */ strlcpy(in_val, "No", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; } else { if (strcasecmp(param->val, "Yes") == 0 || strcasecmp(param->val, "No") == 0) { new_val = param->val; } else { /* unknown value */ strlcpy(in_val, "Reject", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; goto add_val; } } break; case ISPT_DECLARATIVE: strlcpy(in_val, param->val, ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; break; default: strlcpy(in_val, param->val, ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; break; } update_val: if (sw) { /* update session wide */ SESS_MTX_LOCK(conn); istgt_iscsi_param_set(conn->sess->params, param->key, new_val); SESS_MTX_UNLOCK(conn); } else { /* update connection only */ istgt_iscsi_param_set(conn->params, param->key, new_val); } add_val: if (cur_type != ISPT_DECLARATIVE) { if (alloc_len - total < 1) { ISTGT_ERRLOG("data space small %d\n", alloc_len); return total; } ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "negotiated %s=%s\n", param->key, new_val); len = snprintf((char *) data + total, alloc_len - total, "%s=%s", param->key, new_val); total += len + 1; } } xfree(valid_list); xfree(in_val); xfree(cur_val); return total; } static int istgt_iscsi_append_text(CONN_Ptr conn, const char *key, const char *val, uint8_t *data, int alloc_len, int data_len) { int total; int len; total = data_len; if (alloc_len < 1) { return 0; } if (total > alloc_len) { total = alloc_len; data[total - 1] = '\0'; return total; } if (alloc_len - total < 1) { ISTGT_ERRLOG("data space small %d\n", alloc_len); return total; } len = snprintf((char *) data + total, alloc_len - total, "%s=%s", key, val); total += len + 1; return total; } static int istgt_iscsi_append_param(CONN_Ptr conn, const char *key, uint8_t *data, int alloc_len, int data_len) { ISCSI_PARAM *param; int rc; param = istgt_iscsi_param_find(conn->params, key); if (param == NULL) { param = istgt_iscsi_param_find(conn->sess->params, key); if (param == NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "no key %.64s\n", key); return data_len; } } rc = istgt_iscsi_append_text(conn, param->key, param->val, data, alloc_len, data_len); return rc; } int istgt_chap_get_authinfo(ISTGT_CHAP_AUTH *auth, const char *authfile, const char *authuser, int ag_tag) { CONFIG *config = NULL; CF_SECTION *sp; const char *val; const char *user, *muser; const char *secret, *msecret; int rc; int i; if (auth->user != NULL) { xfree(auth->user); xfree(auth->secret); xfree(auth->muser); xfree(auth->msecret); auth->user = auth->secret = NULL; auth->muser = auth->msecret = NULL; } /* read config files */ config = istgt_allocate_config(); rc = istgt_read_config(config, authfile); if (rc < 0) { ISTGT_ERRLOG("auth conf error\n"); istgt_free_config(config); return -1; } //istgt_print_config(config); sp = config->section; while (sp != NULL) { if (sp->type == ST_AUTHGROUP) { if (sp->num == 0) { ISTGT_ERRLOG("Group 0 is invalid\n"); istgt_free_config(config); return -1; } if (ag_tag != sp->num) { goto skip_ag_tag; } val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Auth", i); if (val == NULL) break; user = istgt_get_nmval(sp, "Auth", i, 0); secret = istgt_get_nmval(sp, "Auth", i, 1); muser = istgt_get_nmval(sp, "Auth", i, 2); msecret = istgt_get_nmval(sp, "Auth", i, 3); if (strcasecmp(authuser, user) == 0) { /* match user */ auth->user = xstrdup(user); auth->secret = xstrdup(secret); auth->muser = xstrdup(muser); auth->msecret = xstrdup(msecret); istgt_free_config(config); return 0; } } } skip_ag_tag: sp = sp->next; } istgt_free_config(config); return 0; } static int istgt_iscsi_get_authinfo(CONN_Ptr conn, const char *authuser) { char *authfile = NULL; int ag_tag; int rc; SESS_MTX_LOCK(conn); if (conn->sess->lu != NULL) { ag_tag = conn->sess->lu->auth_group; } else { ag_tag = -1; } SESS_MTX_UNLOCK(conn); if (ag_tag < 0) { MTX_LOCK(&conn->istgt->mutex); ag_tag = conn->istgt->discovery_auth_group; MTX_UNLOCK(&conn->istgt->mutex); } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ag_tag=%d\n", ag_tag); MTX_LOCK(&conn->istgt->mutex); authfile = xstrdup(conn->istgt->authfile); MTX_UNLOCK(&conn->istgt->mutex); rc = istgt_chap_get_authinfo(&conn->auth, authfile, authuser, ag_tag); if (rc < 0) { ISTGT_ERRLOG("chap_get_authinfo() failed\n"); xfree(authfile); return -1; } xfree(authfile); return 0; } static int istgt_iscsi_auth_params(CONN_Ptr conn, ISCSI_PARAM *params, const char *method, uint8_t *data, int alloc_len, int data_len) { char *in_val; char *in_next; char *new_val; const char *val; const char *user; const char *response; const char *challenge; int total; int rc; if (conn == NULL || params == NULL || method == NULL) { return -1; } if (strcasecmp(method, "CHAP") == 0) { /* method OK */ } else { ISTGT_ERRLOG("unsupported AuthMethod %.64s\n", method); return -1; } total = data_len; if (alloc_len < 1) { return 0; } if (total > alloc_len) { total = alloc_len; data[total - 1] = '\0'; return total; } /* for temporary store */ in_val = xmalloc(ISCSI_TEXT_MAX_VAL_LEN + 1); /* CHAP method (RFC1994) */ if ((val = ISCSI_GETVAL(params, "CHAP_A")) != NULL) { if (conn->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_A) { ISTGT_ERRLOG("CHAP sequence error\n"); goto error_return; } /* CHAP_A is LIST type */ strlcpy(in_val, val, ISCSI_TEXT_MAX_VAL_LEN); in_next = in_val; while ((new_val = strsepq(&in_next, ",")) != NULL) { if (strcasecmp(new_val, "5") == 0) { /* CHAP with MD5 */ break; } } if (new_val == NULL) { strlcpy(in_val, "Reject", ISCSI_TEXT_MAX_VAL_LEN); new_val = in_val; total = istgt_iscsi_append_text(conn, "CHAP_A", new_val, data, alloc_len, total); goto error_return; } /* selected algorithm is 5 (MD5) */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "got CHAP_A=%s\n", new_val); total = istgt_iscsi_append_text(conn, "CHAP_A", new_val, data, alloc_len, total); /* Identifier is one octet */ istgt_gen_random(conn->auth.chap_id, 1); snprintf(in_val, ISCSI_TEXT_MAX_VAL_LEN, "%d", (int) conn->auth.chap_id[0]); total = istgt_iscsi_append_text(conn, "CHAP_I", in_val, data, alloc_len, total); /* Challenge Value is a variable stream of octets */ /* (binary length MUST not exceed 1024 bytes) */ conn->auth.chap_challenge_len = ISTGT_CHAP_CHALLENGE_LEN; istgt_gen_random(conn->auth.chap_challenge, conn->auth.chap_challenge_len); istgt_bin2hex(in_val, ISCSI_TEXT_MAX_VAL_LEN, conn->auth.chap_challenge, conn->auth.chap_challenge_len); total = istgt_iscsi_append_text(conn, "CHAP_C", in_val, data, alloc_len, total); conn->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_NR; } else if ((val = ISCSI_GETVAL(params, "CHAP_N")) != NULL) { uint8_t resmd5[ISTGT_MD5DIGEST_LEN]; uint8_t tgtmd5[ISTGT_MD5DIGEST_LEN]; ISTGT_MD5CTX md5ctx; user = val; if (conn->auth.chap_phase != ISTGT_CHAP_PHASE_WAIT_NR) { ISTGT_ERRLOG("CHAP sequence error\n"); goto error_return; } response = ISCSI_GETVAL(params, "CHAP_R"); if (response == NULL) { ISTGT_ERRLOG("no response\n"); goto error_return; } rc = istgt_hex2bin(resmd5, ISTGT_MD5DIGEST_LEN, response); if (rc < 0 || rc != ISTGT_MD5DIGEST_LEN) { ISTGT_ERRLOG("response format error\n"); goto error_return; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "got CHAP_N/CHAP_R\n"); rc = istgt_iscsi_get_authinfo(conn, val); if (rc < 0) { //ISTGT_ERRLOG("auth user or secret is missing\n"); ISTGT_ERRLOG("iscsi_get_authinfo() failed\n"); goto error_return; } if (conn->auth.user == NULL || conn->auth.secret == NULL) { //ISTGT_ERRLOG("auth user or secret is missing\n"); ISTGT_ERRLOG("auth failed (user %.64s)\n", user); goto error_return; } istgt_md5init(&md5ctx); /* Identifier */ istgt_md5update(&md5ctx, conn->auth.chap_id, 1); /* followed by secret */ istgt_md5update(&md5ctx, conn->auth.secret, strlen(conn->auth.secret)); /* followed by Challenge Value */ istgt_md5update(&md5ctx, conn->auth.chap_challenge, conn->auth.chap_challenge_len); /* tgtmd5 is expecting Response Value */ istgt_md5final(tgtmd5, &md5ctx); istgt_bin2hex(in_val, ISCSI_TEXT_MAX_VAL_LEN, tgtmd5, ISTGT_MD5DIGEST_LEN); #if 0 printf("tgtmd5=%s, resmd5=%s\n", in_val, response); istgt_dump("tgtmd5", tgtmd5, ISTGT_MD5DIGEST_LEN); istgt_dump("resmd5", resmd5, ISTGT_MD5DIGEST_LEN); #endif /* compare MD5 digest */ if (memcmp(tgtmd5, resmd5, ISTGT_MD5DIGEST_LEN) != 0) { /* not match */ //ISTGT_ERRLOG("auth user or secret is missing\n"); ISTGT_ERRLOG("auth failed (user %.64s)\n", user); goto error_return; } /* OK initiator's secret */ conn->authenticated = 1; /* mutual CHAP? */ val = ISCSI_GETVAL(params, "CHAP_I"); if (val != NULL) { conn->auth.chap_mid[0] = (uint8_t) strtol(val, NULL, 10); challenge = ISCSI_GETVAL(params, "CHAP_C"); if (challenge == NULL) { ISTGT_ERRLOG("CHAP sequence error\n"); goto error_return; } rc = istgt_hex2bin(conn->auth.chap_mchallenge, ISTGT_CHAP_CHALLENGE_LEN, challenge); if (rc < 0) { ISTGT_ERRLOG("challenge format error\n"); goto error_return; } conn->auth.chap_mchallenge_len = rc; #if 0 istgt_dump("MChallenge", conn->auth.chap_mchallenge, conn->auth.chap_mchallenge_len); #endif ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "got CHAP_I/CHAP_C\n"); if (conn->auth.muser == NULL || conn->auth.msecret == NULL) { //ISTGT_ERRLOG("mutual auth user or secret is missing\n"); ISTGT_ERRLOG("auth failed (user %.64s)\n", user); goto error_return; } istgt_md5init(&md5ctx); /* Identifier */ istgt_md5update(&md5ctx, conn->auth.chap_mid, 1); /* followed by secret */ istgt_md5update(&md5ctx, conn->auth.msecret, strlen(conn->auth.msecret)); /* followed by Challenge Value */ istgt_md5update(&md5ctx, conn->auth.chap_mchallenge, conn->auth.chap_mchallenge_len); /* tgtmd5 is Response Value */ istgt_md5final(tgtmd5, &md5ctx); istgt_bin2hex(in_val, ISCSI_TEXT_MAX_VAL_LEN, tgtmd5, ISTGT_MD5DIGEST_LEN); total = istgt_iscsi_append_text(conn, "CHAP_N", conn->auth.muser, data, alloc_len, total); total = istgt_iscsi_append_text(conn, "CHAP_R", in_val, data, alloc_len, total); } else { /* not mutual */ if (conn->req_mutual) { ISTGT_ERRLOG("required mutual CHAP\n"); goto error_return; } } conn->auth.chap_phase = ISTGT_CHAP_PHASE_END; } else { /* not found CHAP keys */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "start CHAP\n"); conn->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A; } xfree(in_val); return total; error_return: conn->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A; xfree(in_val); return -1; } static int istgt_iscsi_reject(CONN_Ptr conn, ISCSI_PDU_Ptr pdu, int reason) { ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *data; int total_ahs_len; int data_len; int alloc_len; int rc; total_ahs_len = DGET8(&pdu->bhs.total_ahs_len); data_len = 0; alloc_len = ISCSI_BHS_LEN + (4 * total_ahs_len); if (conn->header_digest) { alloc_len += ISCSI_DIGEST_LEN; } data = xmalloc(alloc_len); memset(data, 0, alloc_len); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Reject PDU reason=%d\n", reason); if (conn->sess != NULL) { SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); } else { ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "StatSN=%u\n", conn->StatSN); } memcpy(data, &pdu->bhs, ISCSI_BHS_LEN); data_len += ISCSI_BHS_LEN; if (total_ahs_len != 0) { memcpy(data + data_len, pdu->ahs, (4 * total_ahs_len)); data_len += (4 * total_ahs_len); } if (conn->header_digest) { memcpy(data + data_len, pdu->header_digest, ISCSI_DIGEST_LEN); data_len += ISCSI_DIGEST_LEN; } rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = data; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_REJECT; BDADD8W(&rsp[1], 1, 7, 1); rsp[2] = reason; rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], data_len); // DataSegmentLength DSET32(&rsp[16], 0xffffffffU); DSET32(&rsp[24], conn->StatSN); conn->StatSN++; if (conn->sess != NULL) { SESS_MTX_LOCK(conn); DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); } else { DSET32(&rsp[28], 1); DSET32(&rsp[32], 1); } DSET32(&rsp[36], 0); // DataSN/R2TSN ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "PDU", rsp, ISCSI_BHS_LEN); rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); xfree(data); return -1; } xfree(data); return 0; } static void istgt_iscsi_copy_param2var(CONN_Ptr conn) { const char *val; val = ISCSI_GETVAL(conn->params, "MaxRecvDataSegmentLength"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "copy MaxRecvDataSegmentLength=%s\n", val); conn->MaxRecvDataSegmentLength = (int) strtol(val, NULL, 10); if (conn->sendbufsize != conn->MaxRecvDataSegmentLength) { xfree(conn->recvbuf); xfree(conn->sendbuf); if (conn->MaxRecvDataSegmentLength < 8192) { conn->recvbufsize = 8192; conn->sendbufsize = 8192; } else { conn->recvbufsize = conn->MaxRecvDataSegmentLength; conn->sendbufsize = conn->MaxRecvDataSegmentLength; } conn->recvbuf = xmalloc(conn->recvbufsize); conn->sendbuf = xmalloc(conn->sendbufsize); } val = ISCSI_GETVAL(conn->params, "HeaderDigest"); if (strcasecmp(val, "CRC32C") == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set HeaderDigest=1\n"); conn->header_digest = 1; } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set HeaderDigest=0\n"); conn->header_digest = 0; } val = ISCSI_GETVAL(conn->params, "DataDigest"); if (strcasecmp(val, "CRC32C") == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set DataDigest=1\n"); conn->data_digest = 1; } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set DataDigest=0\n"); conn->data_digest = 0; } SESS_MTX_LOCK(conn); val = ISCSI_GETVAL(conn->sess->params, "MaxConnections"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "copy MaxConnections=%s\n", val); conn->sess->MaxConnections = (int) strtol(val, NULL, 10); val = ISCSI_GETVAL(conn->sess->params, "MaxOutstandingR2T"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "copy MaxOutstandingR2T=%s\n", val); conn->sess->MaxOutstandingR2T = (int) strtol(val, NULL, 10); conn->MaxOutstandingR2T = conn->sess->MaxOutstandingR2T; val = ISCSI_GETVAL(conn->sess->params, "FirstBurstLength"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "copy FirstBurstLength=%s\n", val); conn->sess->FirstBurstLength = (int) strtol(val, NULL, 10); conn->FirstBurstLength = conn->sess->FirstBurstLength; val = ISCSI_GETVAL(conn->sess->params, "MaxBurstLength"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "copy MaxBurstLength=%s\n", val); conn->sess->MaxBurstLength = (int) strtol(val, NULL, 10); conn->MaxBurstLength = conn->sess->MaxBurstLength; val = ISCSI_GETVAL(conn->sess->params, "InitialR2T"); if (strcasecmp(val, "Yes") == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set InitialR2T=1\n"); conn->sess->initial_r2t = 1; } else{ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set InitialR2T=0\n"); conn->sess->initial_r2t = 0; } val = ISCSI_GETVAL(conn->sess->params, "ImmediateData"); if (strcasecmp(val, "Yes") == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set ImmediateData=1\n"); conn->sess->immediate_data = 1; } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set ImmediateData=0\n"); conn->sess->immediate_data = 0; } SESS_MTX_UNLOCK(conn); } static int istgt_iscsi_check_values(CONN_Ptr conn) { SESS_MTX_LOCK(conn); if (conn->sess->FirstBurstLength > conn->sess->MaxBurstLength) { ISTGT_ERRLOG("FirstBurstLength(%d) > MaxBurstLength(%d)\n", conn->sess->FirstBurstLength, conn->sess->MaxBurstLength); SESS_MTX_UNLOCK(conn); return -1; } if (conn->sess->MaxBurstLength > 0x00ffffff) { ISTGT_ERRLOG("MaxBurstLength(%d) > 0x00ffffff\n", conn->sess->MaxBurstLength); SESS_MTX_UNLOCK(conn); return -1; } if (conn->TargetMaxRecvDataSegmentLength < 512) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) < 512\n", conn->TargetMaxRecvDataSegmentLength); return -1; } if (conn->TargetMaxRecvDataSegmentLength > 0x00ffffff) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) > 0x00ffffff\n", conn->TargetMaxRecvDataSegmentLength); SESS_MTX_UNLOCK(conn); return -1; } if (conn->MaxRecvDataSegmentLength < 512) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) < 512\n", conn->MaxRecvDataSegmentLength); return -1; } if (conn->MaxRecvDataSegmentLength > 0x00ffffff) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) > 0x00ffffff\n", conn->MaxRecvDataSegmentLength); SESS_MTX_UNLOCK(conn); return -1; } SESS_MTX_UNLOCK(conn); return 0; } static int istgt_iscsi_op_login(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { char buf[MAX_TMPBUF]; ISTGT_LU_Ptr lu = NULL; ISCSI_PARAM *params = NULL; ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *cp; uint8_t *data; const char *session_type; const char *auth_method; const char *val; uint64_t isid; uint16_t tsih; uint16_t cid; uint32_t task_tag; uint32_t CmdSN; uint32_t ExpStatSN; int T_bit, C_bit; int CSG, NSG; int VersionMin, VersionMax; int StatusClass, StatusDetail; int data_len; int alloc_len; int rc; /* Login is proceeding OK */ StatusClass = 0x00; StatusDetail = 0x00; data_len = 0; if (conn->MaxRecvDataSegmentLength < 8192) { // Default MaxRecvDataSegmentLength - RFC3720(12.12) alloc_len = 8192; } else { alloc_len = conn->MaxRecvDataSegmentLength; } data = xmalloc(alloc_len); memset(data, 0, alloc_len); cp = (uint8_t *) &pdu->bhs; T_bit = BGET8(&cp[1], 7); C_bit = BGET8(&cp[1], 6); CSG = BGET8W(&cp[1], 3, 2); NSG = BGET8W(&cp[1], 1, 2); VersionMin = cp[2]; VersionMax = cp[3]; isid = DGET48(&cp[8]); tsih = DGET16(&cp[14]); cid = DGET16(&cp[20]); task_tag = DGET32(&cp[16]); CmdSN = DGET32(&cp[24]); ExpStatSN = DGET32(&cp[28]); #if 1 ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "PDU", cp, ISCSI_BHS_LEN); #endif ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "T=%d, C=%d, CSG=%d, NSG=%d, Min=%d, Max=%d, ITT=%x\n", T_bit, C_bit, CSG, NSG, VersionMin, VersionMax, task_tag); if (conn->sess != NULL) { SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", CmdSN, ExpStatSN, conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); } else { ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u\n", CmdSN, ExpStatSN, conn->StatSN); } if (T_bit && C_bit) { ISTGT_ERRLOG("transit error\n"); xfree(data); return -1; } if (VersionMin > ISCSI_VERSION || VersionMax < ISCSI_VERSION) { ISTGT_ERRLOG("unsupported version %d/%d\n", VersionMin, VersionMax); /* Unsupported version */ StatusClass = 0x02; StatusDetail = 0x05; goto response; } /* store incoming parameters */ rc = istgt_iscsi_parse_params(¶ms, pdu->data, pdu->data_segment_len); if (rc < 0) { ISTGT_ERRLOG("iscsi_parse_params() failed\n"); error_return: istgt_iscsi_param_free(params); xfree(data); return -1; } /* set port identifiers and parameters */ if (conn->login_phase == ISCSI_LOGIN_PHASE_NONE) { /* Initiator Name and Port */ val = ISCSI_GETVAL(params, "InitiatorName"); if (val == NULL) { ISTGT_ERRLOG("InitiatorName is empty\n"); /* Missing parameter */ StatusClass = 0x02; StatusDetail = 0x07; goto response; } snprintf(conn->initiator_name, sizeof conn->initiator_name, "%s", val); snprintf(conn->initiator_port, sizeof conn->initiator_port, "%s" ",i,0x" "%12.12" PRIx64, val, isid); strlwr(conn->initiator_name); strlwr(conn->initiator_port); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Initiator name: %s\n", conn->initiator_name); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Initiator port: %s\n", conn->initiator_port); /* Session Type */ session_type = ISCSI_GETVAL(params, "SessionType"); if (session_type == NULL) { if (tsih != 0) { session_type = "Normal"; } else { ISTGT_ERRLOG("SessionType is empty\n"); /* Missing parameter */ StatusClass = 0x02; StatusDetail = 0x07; goto response; } } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Session Type: %s\n", session_type); /* Target Name and Port */ if (strcasecmp(session_type, "Normal") == 0) { val = ISCSI_GETVAL(params, "TargetName"); if (val == NULL) { ISTGT_ERRLOG("TargetName is empty\n"); /* Missing parameter */ StatusClass = 0x02; StatusDetail = 0x07; goto response; } snprintf(conn->target_name, sizeof conn->target_name, "%s", val); snprintf(conn->target_port, sizeof conn->target_port, "%s" ",t,0x" "%4.4x", val, conn->portal.tag); strlwr(conn->target_name); strlwr(conn->target_port); MTX_LOCK(&conn->istgt->mutex); lu = istgt_lu_find_target(conn->istgt, conn->target_name); if (lu == NULL) { MTX_UNLOCK(&conn->istgt->mutex); ISTGT_ERRLOG("lu_find_target() failed\n"); /* Not found */ StatusClass = 0x02; StatusDetail = 0x03; goto response; } rc = istgt_lu_access(conn, lu, conn->initiator_name, conn->initiator_addr); if (rc < 0) { MTX_UNLOCK(&conn->istgt->mutex); ISTGT_ERRLOG("lu_access() failed\n"); /* Not found */ StatusClass = 0x02; StatusDetail = 0x03; goto response; } if (rc == 0) { MTX_UNLOCK(&conn->istgt->mutex); ISTGT_ERRLOG("access denied\n"); /* Not found */ StatusClass = 0x02; StatusDetail = 0x03; goto response; } MTX_UNLOCK(&conn->istgt->mutex); /* check existing session */ ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "isid=%"PRIx64", tsih=%u, cid=%u\n", isid, tsih, cid); if (tsih != 0) { /* multiple connections */ rc = istgt_append_sess(conn, isid, tsih, cid); if (rc < 0) { ISTGT_ERRLOG("isid=%"PRIx64", tsih=%u, cid=%u: " "append_sess() failed\n", isid, tsih, cid); /* Can't include in session */ StatusClass = 0x02; StatusDetail = 0x08; goto response; } } else { /* new session, drop old sess by the initiator */ istgt_iscsi_drop_old_conns(conn); } /* force target flags */ MTX_LOCK(&lu->mutex); if (lu->no_auth_chap) { conn->req_auth = 0; rc = istgt_iscsi_param_del(&conn->params, "AuthMethod"); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_del() failed\n"); goto error_return; } rc = istgt_iscsi_param_add(&conn->params, "AuthMethod", "None", "None", ISPT_LIST); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_add() failed\n"); goto error_return; } } else if (lu->auth_chap) { conn->req_auth = 1; rc = istgt_iscsi_param_del(&conn->params, "AuthMethod"); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_del() failed\n"); goto error_return; } rc = istgt_iscsi_param_add(&conn->params, "AuthMethod", "CHAP", "CHAP", ISPT_LIST); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_add() failed\n"); goto error_return; } } if (lu->auth_chap_mutual) { conn->req_mutual = 1; } if (lu->header_digest) { rc = istgt_iscsi_param_del(&conn->params, "HeaderDigest"); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_del() failed\n"); goto error_return; } rc = istgt_iscsi_param_add(&conn->params, "HeaderDigest", "CRC32C", "CRC32C", ISPT_LIST); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_add() failed\n"); goto error_return; } } if (lu->data_digest) { rc = istgt_iscsi_param_del(&conn->params, "DataDigest"); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_del() failed\n"); goto error_return; } rc = istgt_iscsi_param_add(&conn->params, "DataDigest", "CRC32C", "CRC32C", ISPT_LIST); if (rc < 0) { MTX_UNLOCK(&lu->mutex); ISTGT_ERRLOG("iscsi_param_add() failed\n"); goto error_return; } } MTX_UNLOCK(&lu->mutex); } else if (strcasecmp(session_type, "Discovery") == 0) { snprintf(conn->target_name, sizeof conn->target_name, "%s", "dummy"); snprintf(conn->target_port, sizeof conn->target_port, "%s" ",t,0x" "%4.4x", "dummy", conn->portal.tag); lu = NULL; tsih = 0; /* force target flags */ MTX_LOCK(&conn->istgt->mutex); if (conn->istgt->no_discovery_auth) { conn->req_auth = 0; rc = istgt_iscsi_param_del(&conn->params, "AuthMethod"); if (rc < 0) { MTX_UNLOCK(&conn->istgt->mutex); ISTGT_ERRLOG("iscsi_param_del() failed\n"); goto error_return; } rc = istgt_iscsi_param_add(&conn->params, "AuthMethod", "None", "None", ISPT_LIST); if (rc < 0) { MTX_UNLOCK(&conn->istgt->mutex); ISTGT_ERRLOG("iscsi_param_add() failed\n"); goto error_return; } } else if (conn->istgt->req_discovery_auth) { conn->req_auth = 1; rc = istgt_iscsi_param_del(&conn->params, "AuthMethod"); if (rc < 0) { MTX_UNLOCK(&conn->istgt->mutex); ISTGT_ERRLOG("iscsi_param_del() failed\n"); goto error_return; } rc = istgt_iscsi_param_add(&conn->params, "AuthMethod", "CHAP", "CHAP", ISPT_LIST); if (rc < 0) { MTX_UNLOCK(&conn->istgt->mutex); ISTGT_ERRLOG("iscsi_param_add() failed\n"); goto error_return; } } if (conn->istgt->req_discovery_auth_mutual) { conn->req_mutual = 1; } MTX_UNLOCK(&conn->istgt->mutex); } else { ISTGT_ERRLOG("unknown session type\n"); /* Missing parameter */ StatusClass = 0x02; StatusDetail = 0x07; goto response; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Target name: %s\n", conn->target_name); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Target port: %s\n", conn->target_port); conn->authenticated = 0; conn->auth.chap_phase = ISTGT_CHAP_PHASE_WAIT_A; conn->cid = cid; if (lu == NULL || lu->queue_depth == 0) { conn->queue_depth = ISCMDQ; } else { conn->queue_depth = lu->queue_depth; } conn->max_pending = (conn->queue_depth + 1) * 2; #if 0 /* override config setting */ MTX_LOCK(&conn->r2t_mutex); if ((conn->max_r2t > 0) && (conn->max_r2t < conn->max_pending)) { int i; xfree(conn->r2t_tasks); conn->max_r2t = conn->max_pending; conn->r2t_tasks = xmalloc (sizeof *conn->r2t_tasks * (conn->max_r2t + 1)); for (i = 0; i < (conn->max_r2t + 1); i++) { conn->r2t_tasks[i] = NULL; } } MTX_UNLOCK(&conn->r2t_mutex); #endif if (conn->sess == NULL) { /* new session */ rc = istgt_create_sess(conn->istgt, conn, lu); if (rc < 0) { ISTGT_ERRLOG("create_sess() failed\n"); goto error_return; } /* initialize parameters */ conn->StatSN = ExpStatSN; SESS_MTX_LOCK(conn); conn->MaxOutstandingR2T = conn->sess->MaxOutstandingR2T; conn->sess->isid = isid; conn->sess->tsih = tsih; conn->sess->lu = lu; conn->sess->ExpCmdSN = CmdSN; conn->sess->MaxCmdSN = CmdSN + conn->queue_depth - 1; SESS_MTX_UNLOCK(conn); } /* limit conns on discovery session */ if (strcasecmp(session_type, "Discovery") == 0) { SESS_MTX_LOCK(conn); conn->sess->MaxConnections = 1; rc = istgt_iscsi_param_set_int(conn->sess->params, "MaxConnections", conn->sess->MaxConnections); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } } /* declarative parameters */ if (lu != NULL) { MTX_LOCK(&lu->mutex); if (lu->alias != NULL) { snprintf(buf, sizeof buf, "%s", lu->alias); } else { snprintf(buf, sizeof buf, "%s", ""); } MTX_UNLOCK(&lu->mutex); SESS_MTX_LOCK(conn); rc = istgt_iscsi_param_set(conn->sess->params, "TargetAlias", buf); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set() failed\n"); goto error_return; } } snprintf(buf, sizeof buf, "%s:%s,%d", conn->portal.host, conn->portal.port, conn->portal.tag); SESS_MTX_LOCK(conn); rc = istgt_iscsi_param_set(conn->sess->params, "TargetAddress", buf); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set() failed\n"); goto error_return; } snprintf(buf, sizeof buf, "%d", conn->portal.tag); SESS_MTX_LOCK(conn); rc = istgt_iscsi_param_set(conn->sess->params, "TargetPortalGroupTag", buf); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set() failed\n"); goto error_return; } /* write in response */ if (lu != NULL) { SESS_MTX_LOCK(conn); val = ISCSI_GETVAL(conn->sess->params, "TargetAlias"); if (val != NULL && strlen(val) != 0) { data_len = istgt_iscsi_append_param(conn, "TargetAlias", data, alloc_len, data_len); } if (strcasecmp(session_type, "Discovery") == 0) { data_len = istgt_iscsi_append_param(conn, "TargetAddress", data, alloc_len, data_len); } data_len = istgt_iscsi_append_param(conn, "TargetPortalGroupTag", data, alloc_len, data_len); SESS_MTX_UNLOCK(conn); } /* start login phase */ conn->login_phase = ISCSI_LOGIN_PHASE_START; } /* negotiate parameters */ data_len = istgt_iscsi_negotiate_params(conn, params, data, alloc_len, data_len); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "Negotiated Params", data, data_len); switch (CSG) { case 0: /* SecurityNegotiation */ auth_method = ISCSI_GETVAL(conn->params, "AuthMethod"); if (auth_method == NULL) { ISTGT_ERRLOG("AuthMethod is empty\n"); /* Missing parameter */ StatusClass = 0x02; StatusDetail = 0x07; goto response; } if (strcasecmp(auth_method, "None") == 0) { conn->authenticated = 1; } else { rc = istgt_iscsi_auth_params(conn, params, auth_method, data, alloc_len, data_len); if (rc < 0) { ISTGT_ERRLOG("iscsi_auth_params() failed\n"); /* Authentication failure */ StatusClass = 0x02; StatusDetail = 0x01; goto response; } data_len = rc; if (conn->authenticated == 0) { /* not complete */ T_bit = 0; } else { if (conn->auth.chap_phase != ISTGT_CHAP_PHASE_END) { ISTGT_WARNLOG("CHAP phase not complete"); } } #if 0 ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "Negotiated Auth Params", data, data_len); #endif } break; case 1: /* LoginOperationalNegotiation */ if (conn->login_phase == ISCSI_LOGIN_PHASE_START) { if (conn->req_auth) { /* Authentication failure */ StatusClass = 0x02; StatusDetail = 0x01; goto response; } else { /* AuthMethod=None */ conn->authenticated = 1; } } if (conn->authenticated == 0) { ISTGT_ERRLOG("authentication error\n"); /* Authentication failure */ StatusClass = 0x02; StatusDetail = 0x01; goto response; } break; case 3: /* FullFeaturePhase */ ISTGT_ERRLOG("XXX Login in FullFeaturePhase\n"); /* Initiator error */ StatusClass = 0x02; StatusDetail = 0x00; goto response; default: ISTGT_ERRLOG("unknown stage\n"); /* Initiator error */ StatusClass = 0x02; StatusDetail = 0x00; goto response; } if (T_bit) { switch (NSG) { case 0: /* SecurityNegotiation */ conn->login_phase = ISCSI_LOGIN_PHASE_SECURITY; break; case 1: /* LoginOperationalNegotiation */ conn->login_phase = ISCSI_LOGIN_PHASE_OPERATIONAL; break; case 3: /* FullFeaturePhase */ conn->login_phase = ISCSI_LOGIN_PHASE_FULLFEATURE; SESS_MTX_LOCK(conn); if (ISCSI_EQVAL(conn->sess->params, "SessionType", "Normal")) { /* normal session */ tsih = conn->sess->tsih; /* new tsih? */ if (tsih == 0) { tsih = istgt_lu_allocate_tsih(conn->sess->lu, conn->initiator_port, conn->portal.tag); if (tsih == 0) { SESS_MTX_UNLOCK(conn); ISTGT_ERRLOG("lu_allocate_tsih() failed\n"); goto error_return; } conn->sess->tsih = tsih; } else { /* multiple connection */ } snprintf(buf, sizeof buf, "Login from %s (%s) on %s LU%d" " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u," " CID=%u, HeaderDigest=%s, DataDigest=%s\n", conn->initiator_name, conn->initiator_addr, conn->target_name, conn->sess->lu->num, conn->portal.host, conn->portal.port, conn->portal.tag, conn->sess->isid, conn->sess->tsih, conn->cid, (ISCSI_EQVAL(conn->params, "HeaderDigest", "CRC32C") ? "on" : "off"), (ISCSI_EQVAL(conn->params, "DataDigest", "CRC32C") ? "on" : "off")); ISTGT_NOTICELOG("%s", buf); } else if (ISCSI_EQVAL(conn->sess->params, "SessionType", "Discovery")) { /* discovery session */ /* new tsih */ MTX_LOCK(&g_last_tsih_mutex); tsih = conn->sess->tsih; g_last_tsih++; tsih = g_last_tsih; if (tsih == 0) { g_last_tsih++; tsih = g_last_tsih; } conn->sess->tsih = tsih; MTX_UNLOCK(&g_last_tsih_mutex); snprintf(buf, sizeof buf, "Login(discovery) from %s (%s) on" " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u," " CID=%u, HeaderDigest=%s, DataDigest=%s\n", conn->initiator_name, conn->initiator_addr, conn->portal.host, conn->portal.port, conn->portal.tag, conn->sess->isid, conn->sess->tsih, conn->cid, (ISCSI_EQVAL(conn->params, "HeaderDigest", "CRC32C") ? "on" : "off"), (ISCSI_EQVAL(conn->params, "DataDigest", "CRC32C") ? "on" : "off")); ISTGT_NOTICELOG("%s", buf); } else { ISTGT_ERRLOG("unknown session type\n"); SESS_MTX_UNLOCK(conn); /* Initiator error */ StatusClass = 0x02; StatusDetail = 0x00; goto response; } SESS_MTX_UNLOCK(conn); conn->full_feature = 1; break; default: ISTGT_ERRLOG("unknown stage\n"); /* Initiator error */ StatusClass = 0x02; StatusDetail = 0x00; goto response; } } response: /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = data; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_LOGIN_RSP; BDADD8(&rsp[1], T_bit, 7); BDADD8(&rsp[1], C_bit, 6); BDADD8W(&rsp[1], CSG, 3, 2); BDADD8W(&rsp[1], NSG, 1, 2); rsp[2] = ISCSI_VERSION; // Version-max rsp[3] = ISCSI_VERSION; // Version-active rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], data_len); // DataSegmentLength DSET48(&rsp[8], isid); DSET16(&rsp[14], tsih); DSET32(&rsp[16], task_tag); DSET32(&rsp[24], conn->StatSN); conn->StatSN++; if (conn->sess != NULL) { SESS_MTX_LOCK(conn); DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); } else { DSET32(&rsp[28], CmdSN); DSET32(&rsp[32], CmdSN); } rsp[36] = StatusClass; rsp[37] = StatusDetail; #if 1 ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "PDU", rsp, ISCSI_BHS_LEN); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "DATA", data, data_len); #endif rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); istgt_iscsi_param_free(params); xfree(data); return -1; } /* after send PDU digest on/off */ if (conn->full_feature) { /* update internal variables */ istgt_iscsi_copy_param2var(conn); /* check value */ rc = istgt_iscsi_check_values(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_check_values() failed\n"); istgt_iscsi_param_free(params); xfree(data); return -1; } } istgt_iscsi_param_free(params); xfree(data); return 0; } static int istgt_iscsi_op_text(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { ISCSI_PARAM *params = NULL; ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *cp; uint8_t *data; uint64_t lun; uint32_t task_tag; uint32_t transfer_tag; uint32_t CmdSN; uint32_t ExpStatSN; const char *iiqn; const char *val; int I_bit, F_bit, C_bit; int data_len; int alloc_len; int rc; if (!conn->full_feature) { ISTGT_ERRLOG("before Full Feature\n"); return -1; } data_len = 0; alloc_len = conn->sendbufsize; data = (uint8_t *) conn->sendbuf; memset(data, 0, alloc_len); cp = (uint8_t *) &pdu->bhs; I_bit = BGET8(&cp[0], 7); F_bit = BGET8(&cp[1], 7); C_bit = BGET8(&cp[1], 6); lun = DGET64(&cp[8]); task_tag = DGET32(&cp[16]); transfer_tag = DGET32(&cp[20]); CmdSN = DGET32(&cp[24]); ExpStatSN = DGET32(&cp[28]); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "I=%d, F=%d, C=%d, ITT=%x, TTT=%x\n", I_bit, F_bit, C_bit, task_tag, transfer_tag); SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", CmdSN, ExpStatSN, conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); if (I_bit == 0) { if (SN32_LT(CmdSN, conn->sess->ExpCmdSN) || SN32_GT(CmdSN, conn->sess->MaxCmdSN)) { ISTGT_ERRLOG("CmdSN(%u) ignore (ExpCmdSN=%u, MaxCmdSN=%u)\n", CmdSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); return -1; } } else if (CmdSN != conn->sess->ExpCmdSN) { SESS_MTX_UNLOCK(conn); ISTGT_ERRLOG("CmdSN(%u) error\n", CmdSN); return -1; } SESS_MTX_UNLOCK(conn); if (SN32_GT(ExpStatSN, conn->StatSN)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "StatSN(%u) advanced\n", ExpStatSN); conn->StatSN = ExpStatSN; } if (ExpStatSN != conn->StatSN) { #if 0 ISTGT_ERRLOG("StatSN(%u) error\n", ExpStatSN); return -1; #else /* StarPort have a bug */ ISTGT_WARNLOG("StatSN(%u) rewound\n", ExpStatSN); conn->StatSN = ExpStatSN; #endif } if (F_bit && C_bit) { ISTGT_ERRLOG("final and continue\n"); return -1; } /* store incoming parameters */ rc = istgt_iscsi_parse_params(¶ms, pdu->data, pdu->data_segment_len); if (rc < 0) { ISTGT_ERRLOG("iscsi_parse_params() failed\n"); istgt_iscsi_param_free(params); return -1; } /* negotiate parameters */ data_len = istgt_iscsi_negotiate_params(conn, params, data, alloc_len, data_len); /* sendtargets is special case */ val = ISCSI_GETVAL(params, "SendTargets"); if (val != NULL) { if (strcasecmp(val, "") == 0) { val = conn->target_name; } SESS_MTX_LOCK(conn); iiqn = ISCSI_GETVAL(conn->sess->params, "InitiatorName"); if (ISCSI_EQVAL(conn->sess->params, "SessionType", "Discovery")) { data_len = istgt_lu_sendtargets(conn, conn->initiator_name, conn->initiator_addr, val, data, alloc_len, data_len); } else { if (strcasecmp(val, "ALL") == 0) { /* not in discovery session */ data_len = istgt_iscsi_append_text(conn, "SendTargets", "Reject", data, alloc_len, data_len); } else { data_len = istgt_lu_sendtargets(conn, conn->initiator_name, conn->initiator_addr, val, data, alloc_len, data_len); } } SESS_MTX_UNLOCK(conn); } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "Negotiated Params", data, data_len); /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = data; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_TEXT_RSP; BDADD8(&rsp[1], F_bit, 7); BDADD8(&rsp[1], C_bit, 6); rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], data_len); // DataSegmentLength DSET64(&rsp[8], lun); DSET32(&rsp[16], task_tag); if (F_bit) { DSET32(&rsp[20], 0xffffffffU); } else { transfer_tag = 1 + conn->id; DSET32(&rsp[20], transfer_tag); } DSET32(&rsp[24], conn->StatSN); conn->StatSN++; SESS_MTX_LOCK(conn); if (I_bit == 0) { conn->sess->ExpCmdSN++; conn->sess->MaxCmdSN++; } DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); istgt_iscsi_param_free(params); return -1; } /* update internal variables */ istgt_iscsi_copy_param2var(conn); /* check value */ rc = istgt_iscsi_check_values(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_check_values() failed\n"); istgt_iscsi_param_free(params); return -1; } istgt_iscsi_param_free(params); return 0; } static int istgt_iscsi_op_logout(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { char buf[MAX_TMPBUF]; ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *cp; uint8_t *data; uint32_t task_tag; uint16_t cid; uint32_t CmdSN; uint32_t ExpStatSN; int reason; int response; int data_len; int alloc_len; int rc; data_len = 0; alloc_len = conn->sendbufsize; data = (uint8_t *) conn->sendbuf; memset(data, 0, alloc_len); cp = (uint8_t *) &pdu->bhs; reason = BGET8W(&cp[1], 6, 7); task_tag = DGET32(&cp[16]); cid = DGET16(&cp[20]); CmdSN = DGET32(&cp[24]); ExpStatSN = DGET32(&cp[28]); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "reason=%d, ITT=%x, cid=%d\n", reason, task_tag, cid); if (conn->sess != NULL) { SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", CmdSN, ExpStatSN, conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); if (CmdSN != conn->sess->ExpCmdSN) { ISTGT_WARNLOG("CmdSN(%u) might have dropped\n", CmdSN); /* ignore error */ } SESS_MTX_UNLOCK(conn); } else { ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u\n", CmdSN, ExpStatSN, conn->StatSN); } if (SN32_GT(ExpStatSN, conn->StatSN)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "StatSN(%u) advanced\n", ExpStatSN); conn->StatSN = ExpStatSN; } if (ExpStatSN != conn->StatSN) { ISTGT_WARNLOG("StatSN(%u/%u) might have dropped\n", ExpStatSN, conn->StatSN); /* ignore error */ } response = 0; // connection or session closed successfully /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = data; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_LOGOUT_RSP; BDADD8W(&rsp[1], 1, 7, 1); rsp[2] = response; rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], data_len); // DataSegmentLength DSET32(&rsp[16], task_tag); DSET32(&rsp[24], conn->StatSN); conn->StatSN++; if (conn->sess != NULL) { SESS_MTX_LOCK(conn); if (conn->sess->connections == 1) { conn->sess->ExpCmdSN++; conn->sess->MaxCmdSN++; } DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); } else { DSET32(&rsp[28], CmdSN); DSET32(&rsp[32], CmdSN); } DSET16(&rsp[40], 0); // Time2Wait DSET16(&rsp[42], 0); // Time2Retain rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } SESS_MTX_LOCK(conn); if (ISCSI_EQVAL(conn->sess->params, "SessionType", "Normal")) { snprintf(buf, sizeof buf, "Logout from %s (%s) on %s LU%d" " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u," " CID=%u, HeaderDigest=%s, DataDigest=%s\n", conn->initiator_name, conn->initiator_addr, conn->target_name, conn->sess->lu->num, conn->portal.host, conn->portal.port, conn->portal.tag, conn->sess->isid, conn->sess->tsih, conn->cid, (ISCSI_EQVAL(conn->params, "HeaderDigest", "CRC32C") ? "on" : "off"), (ISCSI_EQVAL(conn->params, "DataDigest", "CRC32C") ? "on" : "off")); ISTGT_NOTICELOG("%s", buf); } else { /* discovery session */ snprintf(buf, sizeof buf, "Logout(discovery) from %s (%s) on" " (%s:%s,%d), ISID=%"PRIx64", TSIH=%u," " CID=%u, HeaderDigest=%s, DataDigest=%s\n", conn->initiator_name, conn->initiator_addr, conn->portal.host, conn->portal.port, conn->portal.tag, conn->sess->isid, conn->sess->tsih, conn->cid, (ISCSI_EQVAL(conn->params, "HeaderDigest", "CRC32C") ? "on" : "off"), (ISCSI_EQVAL(conn->params, "DataDigest", "CRC32C") ? "on" : "off")); ISTGT_NOTICELOG("%s", buf); } SESS_MTX_UNLOCK(conn); conn->exec_logout = 1; return 0; } static int istgt_iscsi_transfer_in_internal(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); static int istgt_iscsi_transfer_in(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { int rc; //MTX_LOCK(&conn->wpdu_mutex); rc = istgt_iscsi_transfer_in_internal(conn, lu_cmd); //MTX_UNLOCK(&conn->wpdu_mutex); return rc; } static int istgt_iscsi_transfer_in_internal(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *data; uint32_t task_tag; uint32_t transfer_tag; uint32_t DataSN; int transfer_len; int data_len; int segment_len; int offset; int F_bit, O_bit, U_bit, S_bit; int residual_len; int sent_status; int len; int rc; data = lu_cmd->data; transfer_len = lu_cmd->transfer_len; data_len = lu_cmd->data_len; segment_len = conn->MaxRecvDataSegmentLength; F_bit = O_bit = U_bit = S_bit = 0; if (data_len < transfer_len) { /* underflow */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Underflow %u/%u\n", data_len, transfer_len); residual_len = transfer_len - data_len; transfer_len = data_len; U_bit = 1; } else if (data_len > transfer_len) { /* overflow */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Overflow %u/%u\n", data_len, transfer_len); residual_len = data_len - transfer_len; O_bit = 1; } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Transfer %u\n", transfer_len); residual_len = 0; } task_tag = lu_cmd->task_tag; transfer_tag = 0xffffffffU; DataSN = 0; sent_status = 0; /* send data splitted by segment_len */ for (offset = 0; offset < transfer_len; offset += segment_len) { len = DMIN32(segment_len, (transfer_len - offset)); if (offset + len > transfer_len) { ISTGT_ERRLOG("transfer missing\n"); return -1; } else if (offset + len == transfer_len) { /* final PDU */ F_bit = 1; S_bit = 0; if (lu_cmd->sense_data_len == 0 && (lu_cmd->status == ISTGT_SCSI_STATUS_GOOD || lu_cmd->status == ISTGT_SCSI_STATUS_CONDITION_MET || lu_cmd->status == ISTGT_SCSI_STATUS_INTERMEDIATE || lu_cmd->status == ISTGT_SCSI_STATUS_INTERMEDIATE_CONDITION_MET)) { S_bit = 1; sent_status = 1; } } else { F_bit = 0; S_bit = 0; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Transfer=%d, Offset=%d, Len=%d\n", transfer_len, offset, len); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "StatSN=%u, DataSN=%u, Offset=%u, Len=%d\n", conn->StatSN, DataSN, offset, len); /* DATA PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = data + offset; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_SCSI_DATAIN; BDADD8(&rsp[1], F_bit, 7); BDADD8(&rsp[1], 0, 6); // A_bit Acknowledge if (F_bit && S_bit) { BDADD8(&rsp[1], O_bit, 2); BDADD8(&rsp[1], U_bit, 1); } else { BDADD8(&rsp[1], 0, 2); BDADD8(&rsp[1], 0, 1); } BDADD8(&rsp[1], S_bit, 0); if (S_bit) { rsp[3] = lu_cmd->status; } else { rsp[3] = 0; // Status or Rsvd } rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], len); // DataSegmentLength DSET32(&rsp[16], task_tag); DSET32(&rsp[20], transfer_tag); if (S_bit) { DSET32(&rsp[24], conn->StatSN); conn->StatSN++; } else { DSET32(&rsp[24], 0); // StatSN or Reserved } SESS_MTX_LOCK(conn); if (F_bit && S_bit && lu_cmd->I_bit == 0) { conn->sess->MaxCmdSN++; } DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); DSET32(&rsp[36], DataSN); DataSN++; DSET32(&rsp[40], (uint32_t) offset); if (F_bit && S_bit) { DSET32(&rsp[44], residual_len); } else { DSET32(&rsp[44], 0); } rc = istgt_iscsi_write_pdu_internal(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } } if (sent_status) { return 1; } return 0; } static int istgt_iscsi_op_scsi(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { ISTGT_LU_CMD lu_cmd; ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *cp; uint8_t *data; uint8_t *cdb; uint64_t lun; uint32_t task_tag; uint32_t transfer_len; uint32_t CmdSN; uint32_t ExpStatSN; int I_bit, F_bit, R_bit, W_bit, Attr_bit; int o_bit, u_bit, O_bit, U_bit; int bidi_residual_len; int residual_len; int data_len; int alloc_len; int rc; if (!conn->full_feature) { ISTGT_ERRLOG("before Full Feature\n"); return -1; } data_len = 0; alloc_len = conn->sendbufsize; data = (uint8_t *) conn->sendbuf; memset(data, 0, alloc_len); memset(&lu_cmd, 0, sizeof lu_cmd); cp = (uint8_t *) &pdu->bhs; I_bit = BGET8(&cp[0], 6); F_bit = BGET8(&cp[1], 7); R_bit = BGET8(&cp[1], 6); W_bit = BGET8(&cp[1], 5); Attr_bit = BGET8W(&cp[1], 2, 3); lun = DGET64(&cp[8]); task_tag = DGET32(&cp[16]); transfer_len = DGET32(&cp[20]); CmdSN = DGET32(&cp[24]); ExpStatSN = DGET32(&cp[28]); cdb = &cp[32]; ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, 16); #if 0 ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "PDU", cp, ISCSI_BHS_LEN); #endif ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "I=%d, F=%d, R=%d, W=%d, Attr=%d, ITT=%x, TL=%u\n", I_bit, F_bit, R_bit, W_bit, Attr_bit, task_tag, transfer_len); SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", CmdSN, ExpStatSN, conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); if (I_bit == 0) { /* XXX MCS reverse order? */ if (SN32_GT(CmdSN, conn->sess->ExpCmdSN)) { if (conn->sess->connections > 1) { struct timespec abstime; time_t now; SESS_MTX_UNLOCK(conn); now = time(NULL); memset(&abstime, 0, sizeof abstime); abstime.tv_sec = now + (MAX_MCSREVWAIT / 1000); abstime.tv_nsec = (MAX_MCSREVWAIT % 1000) * 1000000; rc = 0; SESS_MTX_LOCK(conn); while (SN32_GT(CmdSN, conn->sess->ExpCmdSN)) { conn->sess->req_mcs_cond++; rc = pthread_cond_timedwait(&conn->sess->mcs_cond, &conn->sess->mutex, &abstime); if (rc == ETIMEDOUT) { if (SN32_GT(CmdSN, conn->sess->ExpCmdSN)) { rc = -1; /* timeout */ break; } /* OK cond */ rc = 0; break; } if (rc != 0) { break; } } if (rc < 0) { ISTGT_ERRLOG("MCS: CmdSN(%u) error ExpCmdSN=%u\n", CmdSN, conn->sess->ExpCmdSN); SESS_MTX_UNLOCK(conn); return -1; } #if 0 ISTGT_WARNLOG("MCS: reverse CmdSN=%u(retry=%d, yields=%d)\n", CmdSN, retry, try_yields); #endif } } } if (I_bit == 0) { if (SN32_LT(CmdSN, conn->sess->ExpCmdSN) || SN32_GT(CmdSN, conn->sess->MaxCmdSN)) { ISTGT_ERRLOG("CmdSN(%u) ignore (ExpCmdSN=%u, MaxCmdSN=%u)\n", CmdSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); return -1; } if (SN32_GT(CmdSN, conn->sess->ExpCmdSN)) { ISTGT_WARNLOG("CmdSN(%u) > ExpCmdSN(%u)\n", CmdSN, conn->sess->ExpCmdSN); conn->sess->ExpCmdSN = CmdSN; } } else if (CmdSN != conn->sess->ExpCmdSN) { SESS_MTX_UNLOCK(conn); ISTGT_ERRLOG("CmdSN(%u) error ExpCmdSN=%u\n", CmdSN, conn->sess->ExpCmdSN); return -1; } SESS_MTX_UNLOCK(conn); if (SN32_GT(ExpStatSN, conn->StatSN)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "StatSN(%u) advanced\n", ExpStatSN); conn->StatSN = ExpStatSN; } { uint32_t QCmdSN; SESS_MTX_LOCK(conn); QCmdSN = conn->sess->MaxCmdSN - conn->sess->ExpCmdSN + 1; SESS_MTX_UNLOCK(conn); QCmdSN += conn->queue_depth; if (SN32_LT(ExpStatSN + QCmdSN, conn->StatSN)) { ISTGT_ERRLOG("StatSN(%u/%u) QCmdSN(%u) error\n", ExpStatSN, conn->StatSN, QCmdSN); return -1; } } lu_cmd.pdu = pdu; SESS_MTX_LOCK(conn); lu_cmd.lu = conn->sess->lu; if (I_bit == 0) { conn->sess->ExpCmdSN++; if (conn->sess->req_mcs_cond > 0) { conn->sess->req_mcs_cond--; rc = pthread_cond_broadcast(&conn->sess->mcs_cond); if (rc != 0) { SESS_MTX_UNLOCK(conn); ISTGT_ERRLOG("cond_broadcast() failed\n"); return -1; } } } SESS_MTX_UNLOCK(conn); if (R_bit != 0 && W_bit != 0) { ISTGT_ERRLOG("Bidirectional CDB is not supported\n"); return -1; } lu_cmd.I_bit = I_bit; lu_cmd.F_bit = F_bit; lu_cmd.R_bit = R_bit; lu_cmd.W_bit = W_bit; lu_cmd.Attr_bit = Attr_bit; lu_cmd.lun = lun; lu_cmd.task_tag = task_tag; lu_cmd.transfer_len = transfer_len; lu_cmd.CmdSN = CmdSN; lu_cmd.cdb = cdb; lu_cmd.iobuf = conn->iobuf; lu_cmd.iobufsize = conn->iobufsize; lu_cmd.data = data; lu_cmd.data_len = 0; lu_cmd.alloc_len = alloc_len; lu_cmd.sense_data = conn->snsbuf; lu_cmd.sense_data_len = 0; lu_cmd.sense_alloc_len = conn->snsbufsize; /* need R2T? */ if ((W_bit && F_bit) && (conn->max_r2t > 0)) { if (lu_cmd.pdu->data_segment_len < transfer_len) { rc = istgt_add_transfer_task(conn, &lu_cmd); if (rc < 0) { ISTGT_ERRLOG("add_transfer_task() failed\n"); return -1; } } } /* execute SCSI command */ rc = istgt_lu_execute(conn, &lu_cmd); if (rc < 0) { ISTGT_ERRLOG("lu_execute() failed\n"); return -1; } switch (rc) { case ISTGT_LU_TASK_RESULT_QUEUE_OK: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Queue OK\n"); return 0; case ISTGT_LU_TASK_RESULT_QUEUE_FULL: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Queue Full\n"); ISTGT_WARNLOG("Queue Full\n"); break; case ISTGT_LU_TASK_RESULT_IMMEDIATE: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Immediate\n"); break; default: ISTGT_ERRLOG("lu_execute() unknown rc=%d\n", rc); return -1; } /* transfer data from logical unit */ /* (direction is view of initiator side) */ if (lu_cmd.R_bit && (lu_cmd.status == ISTGT_SCSI_STATUS_GOOD || lu_cmd.sense_data_len != 0)) { rc = istgt_iscsi_transfer_in(conn, &lu_cmd); if (rc < 0) { ISTGT_ERRLOG("iscsi_transfer_in() failed\n"); return -1; } if (rc > 0) { /* sent status by last DATAIN PDU */ return 0; } } o_bit = u_bit = O_bit = U_bit = 0; bidi_residual_len = residual_len = 0; data_len = lu_cmd.data_len; if (transfer_len != 0 && lu_cmd.status == ISTGT_SCSI_STATUS_GOOD) { if (data_len < transfer_len) { /* underflow */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Underflow %u/%u\n", data_len, transfer_len); residual_len = transfer_len - data_len; U_bit = 1; } else if (data_len > transfer_len) { /* overflow */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Overflow %u/%u\n", data_len, transfer_len); residual_len = data_len - transfer_len; O_bit = 1; } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Transfer %u\n", transfer_len); } } /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = lu_cmd.sense_data; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_SCSI_RSP; BDADD8(&rsp[1], 1, 7); BDADD8(&rsp[1], o_bit, 4); BDADD8(&rsp[1], u_bit, 3); BDADD8(&rsp[1], O_bit, 2); BDADD8(&rsp[1], U_bit, 1); rsp[2] = 0x00; // Command Completed at Target //rsp[2] = 0x01; // Target Failure rsp[3] = lu_cmd.status; rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], lu_cmd.sense_data_len); // DataSegmentLength DSET32(&rsp[16], task_tag); DSET32(&rsp[20], 0); // SNACK Tag DSET32(&rsp[24], conn->StatSN); conn->StatSN++; SESS_MTX_LOCK(conn); if (I_bit == 0) { conn->sess->MaxCmdSN++; } DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); DSET32(&rsp[36], 0); // ExpDataSN DSET32(&rsp[40], bidi_residual_len); DSET32(&rsp[44], residual_len); rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } return 0; } static int istgt_iscsi_task_transfer_out(CONN_Ptr conn, ISTGT_LU_TASK_Ptr lu_task) { ISTGT_LU_CMD_Ptr lu_cmd; uint32_t transfer_len; int rc; lu_cmd = &lu_task->lu_cmd; transfer_len = lu_cmd->transfer_len; rc = istgt_iscsi_transfer_out(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, transfer_len); return rc; } static int istgt_iscsi_task_response(CONN_Ptr conn, ISTGT_LU_TASK_Ptr lu_task) { ISTGT_LU_CMD_Ptr lu_cmd; ISCSI_PDU rsp_pdu; uint8_t *rsp; uint32_t task_tag; uint32_t transfer_len; uint32_t CmdSN; int I_bit; int o_bit, u_bit, O_bit, U_bit; int bidi_residual_len; int residual_len; int data_len; int rc; lu_cmd = &lu_task->lu_cmd; transfer_len = lu_cmd->transfer_len; task_tag = lu_cmd->task_tag; I_bit = lu_cmd->I_bit; CmdSN = lu_cmd->CmdSN; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SCSI response CmdSN=%u\n", CmdSN); /* transfer data from logical unit */ /* (direction is view of initiator side) */ if (lu_cmd->R_bit && (lu_cmd->status == ISTGT_SCSI_STATUS_GOOD || lu_cmd->sense_data_len != 0)) { if (lu_task->lock) { rc = istgt_iscsi_transfer_in_internal(conn, lu_cmd); } else { rc = istgt_iscsi_transfer_in(conn, lu_cmd); } if (rc < 0) { ISTGT_ERRLOG("iscsi_transfer_in() failed\n"); return -1; } if (rc > 0) { /* sent status by last DATAIN PDU */ return 0; } } o_bit = u_bit = O_bit = U_bit = 0; bidi_residual_len = residual_len = 0; data_len = lu_cmd->data_len; if (transfer_len != 0 && lu_cmd->status == ISTGT_SCSI_STATUS_GOOD) { if (data_len < transfer_len) { /* underflow */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Underflow %u/%u\n", data_len, transfer_len); residual_len = transfer_len - data_len; U_bit = 1; } else if (data_len > transfer_len) { /* overflow */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Overflow %u/%u\n", data_len, transfer_len); residual_len = data_len - transfer_len; O_bit = 1; } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Transfer %u\n", transfer_len); } } /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = lu_cmd->sense_data; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_SCSI_RSP; BDADD8(&rsp[1], 1, 7); BDADD8(&rsp[1], o_bit, 4); BDADD8(&rsp[1], u_bit, 3); BDADD8(&rsp[1], O_bit, 2); BDADD8(&rsp[1], U_bit, 1); rsp[2] = 0x00; // Command Completed at Target //rsp[2] = 0x01; // Target Failure rsp[3] = lu_cmd->status; rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], lu_cmd->sense_data_len); // DataSegmentLength DSET32(&rsp[16], task_tag); DSET32(&rsp[20], 0); // SNACK Tag DSET32(&rsp[24], conn->StatSN); conn->StatSN++; SESS_MTX_LOCK(conn); if (I_bit == 0) { conn->sess->MaxCmdSN++; } DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); DSET32(&rsp[36], 0); // ExpDataSN DSET32(&rsp[40], bidi_residual_len); DSET32(&rsp[44], residual_len); if (lu_task->lock) { rc = istgt_iscsi_write_pdu_internal(conn, &rsp_pdu); } else { rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); } if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } return 0; } static int istgt_iscsi_op_task(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *cp; uint64_t lun; uint32_t task_tag; uint32_t ref_task_tag; uint32_t CmdSN; uint32_t ExpStatSN; uint32_t ref_CmdSN; uint32_t ExpDataSN; int I_bit; int function; int response; int rc; if (!conn->full_feature) { ISTGT_ERRLOG("before Full Feature\n"); return -1; } cp = (uint8_t *) &pdu->bhs; I_bit = BGET8(&cp[0], 6); function = BGET8W(&cp[1], 6, 7); lun = DGET64(&cp[8]); task_tag = DGET32(&cp[16]); ref_task_tag = DGET32(&cp[20]); CmdSN = DGET32(&cp[24]); ExpStatSN = DGET32(&cp[28]); ref_CmdSN = DGET32(&cp[32]); ExpDataSN = DGET32(&cp[36]); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "I=%d, func=%d, ITT=%x, ref TT=%x, LUN=0x%16.16"PRIx64"\n", I_bit, function, task_tag, ref_task_tag, lun); SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", CmdSN, ExpStatSN, conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); if (CmdSN != conn->sess->ExpCmdSN) { ISTGT_WARNLOG("CmdSN(%u) might have dropped\n", conn->sess->ExpCmdSN); conn->sess->ExpCmdSN = CmdSN; } SESS_MTX_UNLOCK(conn); if (SN32_GT(ExpStatSN, conn->StatSN)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "StatSN(%u) advanced\n", ExpStatSN); conn->StatSN = ExpStatSN; } #if 0 /* not need */ if (ExpStatSN != conn->StatSN) { ISTGT_WARNLOG("StatSN(%u/%u) might have dropped\n", ExpStatSN, conn->StatSN); conn->StatSN = ExpStatSN; } #endif response = 0; // Function complete. switch (function) { case ISCSI_TASK_FUNC_ABORT_TASK: ISTGT_LOG("ABORT_TASK\n"); SESS_MTX_LOCK(conn); rc = istgt_lu_clear_task_ITLQ(conn, conn->sess->lu, lun, ref_CmdSN); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("LU reset failed\n"); } istgt_clear_transfer_task(conn, ref_CmdSN); break; case ISCSI_TASK_FUNC_ABORT_TASK_SET: ISTGT_LOG("ABORT_TASK_SET\n"); SESS_MTX_LOCK(conn); rc = istgt_lu_clear_task_ITL(conn, conn->sess->lu, lun); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("LU reset failed\n"); } istgt_clear_all_transfer_task(conn); break; case ISCSI_TASK_FUNC_CLEAR_ACA: ISTGT_LOG("CLEAR_ACA\n"); break; case ISCSI_TASK_FUNC_CLEAR_TASK_SET: ISTGT_LOG("CLEAR_TASK_SET\n"); SESS_MTX_LOCK(conn); rc = istgt_lu_clear_task_ITL(conn, conn->sess->lu, lun); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("LU reset failed\n"); } istgt_clear_all_transfer_task(conn); break; case ISCSI_TASK_FUNC_LOGICAL_UNIT_RESET: ISTGT_LOG("LOGICAL_UNIT_RESET\n"); istgt_iscsi_drop_all_conns(conn); SESS_MTX_LOCK(conn); rc = istgt_lu_reset(conn->sess->lu, lun); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("LU reset failed\n"); } //conn->state = CONN_STATE_EXITING; break; case ISCSI_TASK_FUNC_TARGET_WARM_RESET: ISTGT_LOG("TARGET_WARM_RESET\n"); istgt_iscsi_drop_all_conns(conn); SESS_MTX_LOCK(conn); rc = istgt_lu_reset(conn->sess->lu, lun); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("LU reset failed\n"); } //conn->state = CONN_STATE_EXITING; break; case ISCSI_TASK_FUNC_TARGET_COLD_RESET: ISTGT_LOG("TARGET_COLD_RESET\n"); istgt_iscsi_drop_all_conns(conn); SESS_MTX_LOCK(conn); rc = istgt_lu_reset(conn->sess->lu, lun); SESS_MTX_UNLOCK(conn); if (rc < 0) { ISTGT_ERRLOG("LU reset failed\n"); } conn->state = CONN_STATE_EXITING; break; case ISCSI_TASK_FUNC_TASK_REASSIGN: ISTGT_LOG("TASK_REASSIGN\n"); break; default: ISTGT_ERRLOG("unsupported function %d\n", function); response = 255; // Function rejected. break; } /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = NULL; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_TASK_RSP; BDADD8(&rsp[1], 1, 7); rsp[2] = response; rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], 0); // DataSegmentLength DSET32(&rsp[16], task_tag); DSET32(&rsp[24], conn->StatSN); conn->StatSN++; SESS_MTX_LOCK(conn); if (I_bit == 0) { conn->sess->ExpCmdSN++; conn->sess->MaxCmdSN++; } DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } return 0; } static int istgt_iscsi_op_nopout(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { ISCSI_PDU rsp_pdu; uint8_t *rsp; uint8_t *cp; uint8_t *data; uint64_t lun; uint32_t task_tag; uint32_t transfer_tag; uint32_t CmdSN; uint32_t ExpStatSN; int I_bit; int ping_len; int data_len; int alloc_len; int rc; if (!conn->full_feature) { ISTGT_ERRLOG("before Full Feature\n"); return -1; } data_len = 0; alloc_len = conn->sendbufsize; data = (uint8_t *) conn->sendbuf; memset(data, 0, alloc_len); cp = (uint8_t *) &pdu->bhs; I_bit = BGET8(&cp[0], 6); ping_len = DGET24(&cp[5]); lun = DGET64(&cp[8]); task_tag = DGET32(&cp[16]); transfer_tag = DGET32(&cp[20]); CmdSN = DGET32(&cp[24]); ExpStatSN = DGET32(&cp[28]); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "I=%d, ITT=%x, TTT=%x\n", I_bit, task_tag, transfer_tag); SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "CmdSN=%u, ExpStatSN=%u, StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", CmdSN, ExpStatSN, conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); if (I_bit == 0) { if (SN32_LT(CmdSN, conn->sess->ExpCmdSN) || SN32_GT(CmdSN, conn->sess->MaxCmdSN)) { ISTGT_ERRLOG("CmdSN(%u) ignore (ExpCmdSN=%u, MaxCmdSN=%u)\n", CmdSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); return -1; } } else if (CmdSN != conn->sess->ExpCmdSN) { SESS_MTX_UNLOCK(conn); ISTGT_ERRLOG("CmdSN(%u) error\n", CmdSN); return -1; } SESS_MTX_UNLOCK(conn); if (SN32_GT(ExpStatSN, conn->StatSN)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "StatSN(%u) advanced\n", ExpStatSN); conn->StatSN = ExpStatSN; } { uint32_t QCmdSN; SESS_MTX_LOCK(conn); QCmdSN = conn->sess->MaxCmdSN - conn->sess->ExpCmdSN + 1; SESS_MTX_UNLOCK(conn); QCmdSN += conn->queue_depth; if (SN32_LT(ExpStatSN + QCmdSN, conn->StatSN)) { ISTGT_ERRLOG("StatSN(%u/%u) QCmdSN(%u) error\n", ExpStatSN, conn->StatSN, QCmdSN); return -1; } } if (task_tag == 0xffffffffU) { if (I_bit == 1) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "got NOPOUT ITT=0xffffffff\n"); return 0; } else { ISTGT_ERRLOG("got NOPOUT ITT=0xffffffff, I=0\n"); return -1; } } /* response of NOPOUT */ if (ping_len != 0) { if (ping_len > alloc_len) { data_len = DMIN32(alloc_len, conn->MaxRecvDataSegmentLength); } else { data_len = DMIN32(ping_len, conn->MaxRecvDataSegmentLength); } /* ping data */ memcpy(data, pdu->data, data_len); } transfer_tag = 0xffffffffU; /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = data; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_NOPIN; BDADD8(&rsp[1], 1, 7); rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], data_len); // DataSegmentLength DSET64(&rsp[8], lun); DSET32(&rsp[16], task_tag); DSET32(&rsp[20], transfer_tag); DSET32(&rsp[24], conn->StatSN); conn->StatSN++; SESS_MTX_LOCK(conn); if (I_bit == 0) { conn->sess->ExpCmdSN++; conn->sess->MaxCmdSN++; } DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } return 0; } static ISTGT_R2T_TASK_Ptr istgt_allocate_transfer_task(void) { ISTGT_R2T_TASK_Ptr r2t_task; r2t_task = xmalloc(sizeof *r2t_task); memset(r2t_task, 0, sizeof *r2t_task); r2t_task->conn = NULL; r2t_task->lu = NULL; r2t_task->iobuf = NULL; return r2t_task; } static void istgt_free_transfer_task(ISTGT_R2T_TASK_Ptr r2t_task) { if (r2t_task == NULL) return; xfree(r2t_task->iobuf); xfree(r2t_task); } static ISTGT_R2T_TASK_Ptr istgt_get_transfer_task(CONN_Ptr conn, uint32_t transfer_tag) { ISTGT_R2T_TASK_Ptr r2t_task; int i; MTX_LOCK(&conn->r2t_mutex); if (conn->pending_r2t == 0) { MTX_UNLOCK(&conn->r2t_mutex); return NULL; } for (i = 0; i < conn->pending_r2t; i++) { r2t_task = conn->r2t_tasks[i]; #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "CmdSN=%d, TransferTag=%x/%x\n", r2t_task->CmdSN, r2t_task->transfer_tag, transfer_tag); #endif if (r2t_task->transfer_tag == transfer_tag) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Match index=%d, CmdSN=%d, TransferTag=%x\n", i, r2t_task->CmdSN, r2t_task->transfer_tag); MTX_UNLOCK(&conn->r2t_mutex); return r2t_task; } } MTX_UNLOCK(&conn->r2t_mutex); return NULL; } static int istgt_add_transfer_task(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_R2T_TASK_Ptr r2t_task; uint32_t transfer_len; uint32_t transfer_tag; int first_burst_len; int max_burst_len; int data_len; int offset = 0; int len; int idx; int rc; MTX_LOCK(&conn->r2t_mutex); if (conn->pending_r2t >= conn->max_r2t) { // no slot available, skip now... //ISTGT_WARNLOG("No R2T space available (%d/%d)\n", // conn->pending_r2t, conn->max_r2t); MTX_UNLOCK(&conn->r2t_mutex); return 0; } MTX_UNLOCK(&conn->r2t_mutex); transfer_len = lu_cmd->transfer_len; transfer_tag = lu_cmd->task_tag; data_len = lu_cmd->pdu->data_segment_len; first_burst_len = conn->FirstBurstLength; max_burst_len = conn->MaxBurstLength; offset += data_len; if (offset >= first_burst_len) { len = DMIN32(max_burst_len, (transfer_len - offset)); r2t_task = istgt_allocate_transfer_task(); r2t_task->conn = conn; r2t_task->lu = lu_cmd->lu; r2t_task->lun = lu_cmd->lun; r2t_task->CmdSN = lu_cmd->CmdSN; r2t_task->task_tag = lu_cmd->task_tag; r2t_task->transfer_len = transfer_len; r2t_task->transfer_tag = transfer_tag; r2t_task->iobufsize = lu_cmd->transfer_len + 65536; r2t_task->iobuf = xmalloc(r2t_task->iobufsize); memcpy(r2t_task->iobuf, lu_cmd->pdu->data, data_len); r2t_task->offset = offset; r2t_task->R2TSN = 0; r2t_task->DataSN = 0; r2t_task->F_bit = lu_cmd->F_bit; MTX_LOCK(&conn->r2t_mutex); idx = conn->pending_r2t++; conn->r2t_tasks[idx] = r2t_task; MTX_UNLOCK(&conn->r2t_mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Send R2T(Offset=%d, Tag=%x)\n", r2t_task->offset, r2t_task->transfer_tag); rc = istgt_iscsi_send_r2t(conn, lu_cmd, r2t_task->offset, len, r2t_task->transfer_tag, &r2t_task->R2TSN); if (rc < 0) { ISTGT_ERRLOG("iscsi_send_r2t() failed\n"); return -1; } } return 0; } static void istgt_del_transfer_task(CONN_Ptr conn, ISTGT_R2T_TASK_Ptr r2t_task) { int found = 0; int i; if (r2t_task == NULL) return; MTX_LOCK(&conn->r2t_mutex); if (conn->pending_r2t == 0) { MTX_UNLOCK(&conn->r2t_mutex); return; } for (i = 0; i < conn->pending_r2t; i++) { if (conn->r2t_tasks[i] == r2t_task) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Remove R2T task conn id=%d, index=%d\n", conn->id, i); found = 1; break; } } if (found) { for ( ; i < conn->pending_r2t; i++) { conn->r2t_tasks[i] = conn->r2t_tasks[i + 1]; } conn->pending_r2t--; conn->r2t_tasks[conn->pending_r2t] = NULL; } MTX_UNLOCK(&conn->r2t_mutex); } static void istgt_clear_transfer_task(CONN_Ptr conn, uint32_t CmdSN) { int found = 0; int i; MTX_LOCK(&conn->r2t_mutex); if (conn->pending_r2t == 0) { MTX_UNLOCK(&conn->r2t_mutex); return; } for (i = 0; i < conn->pending_r2t; i++) { if (conn->r2t_tasks[i]->CmdSN == CmdSN) { istgt_free_transfer_task(conn->r2t_tasks[i]); conn->r2t_tasks[i] = NULL; found = 1; break; } } if (found) { for ( ; i < conn->pending_r2t; i++) { conn->r2t_tasks[i] = conn->r2t_tasks[i + 1]; } conn->pending_r2t--; conn->r2t_tasks[conn->pending_r2t] = NULL; } MTX_UNLOCK(&conn->r2t_mutex); } static void istgt_clear_all_transfer_task(CONN_Ptr conn) { int i; MTX_LOCK(&conn->r2t_mutex); if (conn->pending_r2t == 0) { MTX_UNLOCK(&conn->r2t_mutex); return; } for (i = 0; i < conn->pending_r2t; i++) { istgt_free_transfer_task(conn->r2t_tasks[i]); conn->r2t_tasks[i] = NULL; } conn->pending_r2t = 0; MTX_UNLOCK(&conn->r2t_mutex); } static int istgt_iscsi_op_data(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { ISTGT_R2T_TASK_Ptr r2t_task; uint8_t *cp; uint8_t *data; uint64_t lun; uint64_t current_lun; uint32_t current_task_tag; uint32_t current_transfer_tag; uint32_t ExpStatSN; uint32_t task_tag; uint32_t transfer_tag; uint32_t ExpDataSN; uint32_t DataSN; uint32_t buffer_offset; int F_bit; int data_len; int alloc_len; int offset; int rc; if (!conn->full_feature) { ISTGT_ERRLOG("before Full Feature\n"); return -1; } MTX_LOCK(&conn->r2t_mutex); if (conn->pending_r2t == 0) { ISTGT_ERRLOG("No R2T task\n"); MTX_UNLOCK(&conn->r2t_mutex); reject_return: rc = istgt_iscsi_reject(conn, pdu, 0x09); if (rc < 0) { ISTGT_ERRLOG("iscsi_reject() failed\n"); return -1; } return 0; } MTX_UNLOCK(&conn->r2t_mutex); cp = (uint8_t *) &pdu->bhs; F_bit = BGET8(&cp[1], 7); data_len = DGET24(&cp[5]); lun = DGET64(&cp[8]); task_tag = DGET32(&cp[16]); transfer_tag = DGET32(&cp[20]); ExpStatSN = DGET32(&cp[28]); DataSN = DGET32(&cp[36]); buffer_offset = DGET32(&cp[40]); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "pending R2T = %d\n", conn->pending_r2t); r2t_task = istgt_get_transfer_task(conn, transfer_tag); if (r2t_task == NULL) { ISTGT_ERRLOG("Not found R2T task for transfer_tag=%x\n", transfer_tag); goto reject_return; } current_lun = r2t_task->lun; current_task_tag = r2t_task->task_tag; current_transfer_tag = r2t_task->transfer_tag; offset = r2t_task->offset; data = r2t_task->iobuf; alloc_len = r2t_task->iobufsize; ExpDataSN = r2t_task->DataSN; ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "StatSN=%u, ExpStatSN=%u, DataSN=%u, Offset=%u, Data=%d\n", conn->StatSN, ExpStatSN, DataSN, buffer_offset, data_len); if (DataSN != ExpDataSN) { ISTGT_ERRLOG("DataSN(%u) error\n", DataSN); return -1; } if (task_tag != current_task_tag) { ISTGT_ERRLOG("task_tag(%x/%x) error\n", task_tag, current_task_tag); return -1; } if (transfer_tag != current_transfer_tag) { ISTGT_ERRLOG("transfer_tag(%x/%x) error\n", transfer_tag, current_transfer_tag); return -1; } if (buffer_offset != offset) { ISTGT_ERRLOG("offset(%u) error\n", buffer_offset); return -1; } if (buffer_offset + data_len > alloc_len) { ISTGT_ERRLOG("offset error\n"); return -1; } memcpy(data + buffer_offset, pdu->data, data_len); offset += data_len; ExpDataSN++; r2t_task->offset = offset; r2t_task->DataSN = ExpDataSN; r2t_task->F_bit = F_bit; return 0; } static int istgt_iscsi_send_r2t(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, int offset, int len, uint32_t transfer_tag, uint32_t *R2TSN) { ISCSI_PDU rsp_pdu; uint8_t *rsp; int rc; /* R2T PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = NULL; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_R2T; BDADD8(&rsp[1], 1, 7); rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], 0); // DataSegmentLength DSET64(&rsp[8], lu_cmd->lun); DSET32(&rsp[16], lu_cmd->task_tag); DSET32(&rsp[20], transfer_tag); DSET32(&rsp[24], conn->StatSN); SESS_MTX_LOCK(conn); DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); DSET32(&rsp[36], *R2TSN); *R2TSN += 1; DSET32(&rsp[40], (uint32_t) offset); DSET32(&rsp[44], (uint32_t) len); rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } return 0; } int istgt_iscsi_transfer_out(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *data, int alloc_len, int transfer_len) { ISTGT_R2T_TASK_Ptr r2t_task; ISCSI_PDU data_pdu; uint8_t *cp; uint64_t current_lun; uint64_t lun; uint32_t current_task_tag; uint32_t current_transfer_tag; uint32_t ExpDataSN; uint32_t task_tag; uint32_t transfer_tag; uint32_t ExpStatSN; uint32_t DataSN; uint32_t buffer_offset; uint32_t R2TSN; int immediate, opcode; int F_bit; int data_len; int segment_len; int first_burst_len; int max_burst_len; int offset; int len; int r2t_flag; int r2t_offset; int r2t_sent; int rc; current_lun = lu_cmd->lun; current_task_tag = lu_cmd->task_tag; current_transfer_tag = lu_cmd->task_tag; ExpDataSN = 0; segment_len = conn->MaxRecvDataSegmentLength; first_burst_len = conn->FirstBurstLength; max_burst_len = conn->MaxBurstLength; offset = 0; r2t_flag = 0; r2t_offset = 0; r2t_sent = 0; R2TSN = 0; cp = (uint8_t *) &lu_cmd->pdu->bhs; data_len = DGET24(&cp[5]); if (transfer_len > alloc_len) { ISTGT_ERRLOG("transfer_len > alloc_len\n"); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Transfer=%d, First=%d, Max=%d, Segment=%d\n", transfer_len, data_len, max_burst_len, segment_len); r2t_task = istgt_get_transfer_task(conn, current_transfer_tag); if (r2t_task != NULL) { current_lun = r2t_task->lun; current_task_tag = r2t_task->task_tag; current_transfer_tag = r2t_task->transfer_tag; offset = r2t_task->offset; R2TSN = r2t_task->R2TSN; ExpDataSN = r2t_task->DataSN; F_bit = r2t_task->F_bit; r2t_flag = 1; data_len = 0; memcpy(data, r2t_task->iobuf, offset); istgt_del_transfer_task(conn, r2t_task); istgt_free_transfer_task(r2t_task); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Using R2T(%d) offset=%d, DataSN=%d\n", conn->pending_r2t, offset, ExpDataSN); rc = istgt_queue_count(&conn->pending_pdus); if (rc > 0) { if (g_trace_flag) { ISTGT_WARNLOG("pending_pdus > 0\n"); } } if (offset < transfer_len) { if (offset >= (first_burst_len + max_burst_len)) { /* need more data */ r2t_flag = 0; } len = DMIN32(max_burst_len, (transfer_len - offset)); memset(&data_pdu.bhs, 0, ISCSI_BHS_LEN); data_pdu.ahs = NULL; data_pdu.data = NULL; data_pdu.copy_pdu = 0; goto r2t_retry; } else if (offset == transfer_len) { if (F_bit == 0) { ISTGT_ERRLOG("F_bit not set on the last PDU\n"); return -1; } } goto r2t_return; } if (data_len != 0) { if (data_len > first_burst_len) { ISTGT_ERRLOG("data_len > first_burst_len\n"); return -1; } if (offset + data_len > alloc_len) { ISTGT_ERRLOG("offset + data_len > alloc_len\n"); return -1; } memcpy(data + offset, lu_cmd->pdu->data, data_len); offset += data_len; r2t_offset = offset; } if (offset < transfer_len) { len = DMIN32(first_burst_len, (transfer_len - offset)); memset(&data_pdu.bhs, 0, ISCSI_BHS_LEN); data_pdu.ahs = NULL; data_pdu.data = NULL; data_pdu.copy_pdu = 0; do { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Transfer=%d, Offset=%d, Len=%d\n", transfer_len, offset, len); /* send R2T if required */ if (r2t_flag == 0 && (conn->sess->initial_r2t || offset >= first_burst_len)) { len = DMIN32(max_burst_len, (transfer_len - offset)); rc = istgt_iscsi_send_r2t(conn, lu_cmd, offset, len, current_transfer_tag, &R2TSN); if (rc < 0) { ISTGT_ERRLOG("iscsi_send_r2t() failed\n"); error_return: if (data_pdu.copy_pdu == 0) { xfree(data_pdu.ahs); data_pdu.ahs = NULL; if (data_pdu.data != data_pdu.shortdata) { xfree(data_pdu.data); } data_pdu.data = NULL; } return -1; } r2t_flag = 1; r2t_offset = offset; r2t_sent = 1; ExpDataSN = 0; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "R2T, Transfer=%d, Offset=%d, Len=%d\n", transfer_len, offset, len); } else { r2t_sent = 0; } /* transfer by segment_len */ rc = istgt_iscsi_read_pdu(conn, &data_pdu); if (rc < 0) { //ISTGT_ERRLOG("iscsi_read_pdu() failed\n"); ISTGT_ERRLOG("iscsi_read_pdu() failed, r2t_sent=%d\n", r2t_sent); goto error_return; } immediate = BGET8W(&data_pdu.bhs.opcode, 6, 1); opcode = BGET8W(&data_pdu.bhs.opcode, 5, 6); cp = (uint8_t *) &data_pdu.bhs; F_bit = BGET8(&cp[1], 7); data_len = DGET24(&cp[5]); lun = DGET64(&cp[8]); task_tag = DGET32(&cp[16]); transfer_tag = DGET32(&cp[20]); ExpStatSN = DGET32(&cp[28]); DataSN = DGET32(&cp[36]); buffer_offset = DGET32(&cp[40]); /* current tag DATA? */ if (opcode == ISCSI_OP_SCSI_DATAOUT) { if (task_tag != current_task_tag) { not_current_tag: //ISTGT_LOG("not task_tag received\n"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "not task_tag received\n"); rc = istgt_iscsi_op_data(conn, &data_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_data() failed\n"); goto error_return; } if (data_pdu.data != data_pdu.shortdata) { xfree(data_pdu.data); } data_pdu.ahs = NULL; data_pdu.data = NULL; data_pdu.copy_pdu = 0; continue; } if (transfer_tag != current_transfer_tag) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "not transfer_tag received\n"); goto not_current_tag; } } if (opcode != ISCSI_OP_SCSI_DATAOUT) { ISCSI_PDU_Ptr save_pdu; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "non DATAOUT PDU received and pending" " (OP=0x%x)\n", opcode); rc = istgt_queue_count(&conn->pending_pdus); if (rc > conn->max_pending) { ISTGT_ERRLOG("pending queue(%d) is full\n", conn->max_pending); goto error_return; } save_pdu = xmalloc(sizeof *save_pdu); memset(save_pdu, 0, sizeof *save_pdu); rc = istgt_iscsi_copy_pdu(save_pdu, &data_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_copy_pdu() failed\n"); xfree(save_pdu); save_pdu = NULL; goto error_return; } rc = istgt_queue_enqueue(&conn->pending_pdus, save_pdu); if (rc < 0) { ISTGT_ERRLOG("queue_enqueue() failed\n"); xfree(save_pdu->ahs); save_pdu->ahs = NULL; if (save_pdu->data != save_pdu->shortdata) { xfree(save_pdu->data); } save_pdu->data = NULL; xfree(save_pdu); save_pdu = NULL; goto error_return; } data_pdu.ahs = NULL; data_pdu.data = NULL; data_pdu.copy_pdu = 0; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "non DATAOUT PDU pending\n"); continue; } ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "StatSN=%u, " "ExpStatSN=%u, DataSN=%u, Offset=%u, Data=%d\n", conn->StatSN, ExpStatSN, DataSN, buffer_offset, data_len); if (DataSN != ExpDataSN) { ISTGT_ERRLOG("DataSN(%u) error\n", DataSN); goto error_return; } #if 0 /* not check in DATAOUT */ if (ExpStatSN != conn->StatSN) { ISTGT_ERRLOG("StatSN(%u) error\n", conn->StatSN); goto error_return; } #endif #if 0 /* not check in DATAOUT */ if (lun != current_lun) { #if 0 ISTGT_ERRLOG("lun(0x%16.16"PRIx64") error\n", lun); goto error_return; #else ISTGT_WARNLOG("lun(0x%16.16"PRIx64")\n", lun); #endif } #endif if (task_tag != current_task_tag) { ISTGT_ERRLOG("task_tag(%x/%x) error\n", task_tag, current_task_tag); goto error_return; } if (transfer_tag != current_transfer_tag) { ISTGT_ERRLOG("transfer_tag(%x/%x) error\n", transfer_tag, current_transfer_tag); goto error_return; } if (buffer_offset != offset) { ISTGT_ERRLOG("offset(%u) error\n", buffer_offset); goto error_return; } if (buffer_offset + data_len > alloc_len) { ISTGT_ERRLOG("offset error\n"); goto error_return; } memcpy(data + buffer_offset, data_pdu.data, data_len); offset += data_len; len -= data_len; ExpDataSN++; if (r2t_flag == 0 && (offset > first_burst_len)) { ISTGT_ERRLOG("data_len(%d) > first_burst_length(%d)", offset, first_burst_len); goto error_return; } if (F_bit != 0 && len != 0) { if (offset < transfer_len) { r2t_flag = 0; goto r2t_retry; } ISTGT_ERRLOG("Expecting more data %d\n", len); goto error_return; } if (F_bit == 0 && len == 0) { ISTGT_ERRLOG("F_bit not set on the last PDU\n"); goto error_return; } if (len == 0) { r2t_flag = 0; } r2t_retry: if (data_pdu.copy_pdu == 0) { xfree(data_pdu.ahs); data_pdu.ahs = NULL; if (data_pdu.data != data_pdu.shortdata) { xfree(data_pdu.data); } data_pdu.data = NULL; } } while (offset < transfer_len); cp = (uint8_t *) &data_pdu.bhs; F_bit = BGET8(&cp[1], 7); if (F_bit == 0) { ISTGT_ERRLOG("F_bit not set on the last PDU\n"); return -1; } } else { cp = (uint8_t *) &lu_cmd->pdu->bhs; F_bit = BGET8(&cp[1], 7); if (F_bit == 0) { ISTGT_ERRLOG("F_bit not set on the last PDU\n"); return -1; } } r2t_return: ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Transfered=%d, Offset=%d\n", transfer_len, offset); return 0; } static int istgt_iscsi_send_nopin(CONN_Ptr conn) { ISCSI_PDU rsp_pdu; uint8_t *rsp; uint64_t lun; uint32_t task_tag; uint32_t transfer_tag; int rc; if (conn->sess == NULL) { return 0; } if (!conn->full_feature) { ISTGT_ERRLOG("before Full Feature\n"); return -1; } SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "send NOPIN isid=%"PRIx64", tsih=%u, cid=%u\n", conn->sess->isid, conn->sess->tsih, conn->cid); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "StatSN=%u, ExpCmdSN=%u, MaxCmdSN=%u\n", conn->StatSN, conn->sess->ExpCmdSN, conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); /* without wanting NOPOUT */ lun = 0; task_tag = 0xffffffffU; transfer_tag = 0xffffffffU; /* response PDU */ rsp = (uint8_t *) &rsp_pdu.bhs; rsp_pdu.data = NULL; memset(rsp, 0, ISCSI_BHS_LEN); rsp[0] = ISCSI_OP_NOPIN; BDADD8(&rsp[1], 1, 7); rsp[4] = 0; // TotalAHSLength DSET24(&rsp[5], 0); // DataSegmentLength DSET64(&rsp[8], lun); DSET32(&rsp[16], task_tag); DSET32(&rsp[20], transfer_tag); DSET32(&rsp[24], conn->StatSN); SESS_MTX_LOCK(conn); DSET32(&rsp[28], conn->sess->ExpCmdSN); DSET32(&rsp[32], conn->sess->MaxCmdSN); SESS_MTX_UNLOCK(conn); rc = istgt_iscsi_write_pdu(conn, &rsp_pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_write_pdu() failed\n"); return -1; } return 0; } static int istgt_iscsi_execute(CONN_Ptr conn, ISCSI_PDU_Ptr pdu) { int immediate, opcode; int rc; if (pdu == NULL) return -1; immediate = BGET8W(&conn->pdu.bhs.opcode, 6, 1); opcode = BGET8W(&conn->pdu.bhs.opcode, 5, 6); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "opcode %x\n", opcode); switch(opcode) { case ISCSI_OP_NOPOUT: rc = istgt_iscsi_op_nopout(conn, pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_nopout() failed\n"); return -1; } break; case ISCSI_OP_SCSI: rc = istgt_iscsi_op_scsi(conn, pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_scsi() failed\n"); return -1; } break; case ISCSI_OP_TASK: rc = istgt_iscsi_op_task(conn, pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_task() failed\n"); return -1; } break; case ISCSI_OP_LOGIN: rc = istgt_iscsi_op_login(conn, pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_login() failed\n"); return -1; } break; case ISCSI_OP_TEXT: rc = istgt_iscsi_op_text(conn, pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_text() failed\n"); return -1; } break; case ISCSI_OP_LOGOUT: rc = istgt_iscsi_op_logout(conn, pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_logout() failed\n"); return -1; } break; case ISCSI_OP_SCSI_DATAOUT: rc = istgt_iscsi_op_data(conn, pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_op_data() failed\n"); return -1; } break; case ISCSI_OP_SNACK: ISTGT_ERRLOG("got SNACK\n"); goto error_out; default: error_out: ISTGT_ERRLOG("unsupported opcode %x\n", opcode); rc = istgt_iscsi_reject(conn, pdu, 0x04); if (rc < 0) { ISTGT_ERRLOG("iscsi_reject() failed\n"); return -1; } break; } return 0; } static void wait_all_task(CONN_Ptr conn) { ISTGT_LU_TASK_Ptr lu_task; #ifdef ISTGT_USE_KQUEUE int kq; struct kevent kev; struct timespec kev_timeout; #else struct pollfd fds[1]; #endif /* ISTGT_USE_KQUEUE */ int msec = 30 * 1000; int rc; if (conn->running_tasks == 0) return; #ifdef ISTGT_USE_KQUEUE kq = kqueue(); if (kq == -1) { ISTGT_ERRLOG("kqueue() failed\n"); return; } EV_SET(&kev, conn->task_pipe[0], EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return; } #else fds[0].fd = conn->task_pipe[0]; fds[0].events = POLLIN; #endif /* ISTGT_USE_KQUEUE */ /* wait all running tasks */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "waiting task start (%d) (left %d tasks)\n", conn->id, conn->running_tasks); while (1) { #ifdef ISTGT_USE_KQUEUE kev_timeout.tv_sec = msec / 1000; kev_timeout.tv_nsec = (msec % 1000) * 1000000; rc = kevent(kq, NULL, 0, &kev, 1, &kev_timeout); if (rc == -1 && errno == EINTR) { continue; } if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); break; } if (rc == 0) { ISTGT_ERRLOG("waiting task timeout (left %d tasks)\n", conn->running_tasks); break; } #else rc = poll(fds, 1, msec); if (rc == -1 && errno == EINTR) { continue; } if (rc == -1) { ISTGT_ERRLOG("poll() failed\n"); break; } if (rc == 0) { ISTGT_ERRLOG("waiting task timeout (left %d tasks)\n", conn->running_tasks); break; } #endif /* ISTGT_USE_KQUEUE */ #ifdef ISTGT_USE_KQUEUE if (kev.ident == conn->task_pipe[0]) { if (kev.flags & (EV_EOF|EV_ERROR)) { break; } #else if (fds[0].revents & POLLHUP) { break; } if (fds[0].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ char tmp[1]; rc = read(conn->task_pipe[0], tmp, 1); if (rc < 0 || rc == 0 || rc != 1) { ISTGT_ERRLOG("read() failed\n"); break; } MTX_LOCK(&conn->task_queue_mutex); lu_task = istgt_queue_dequeue(&conn->task_queue); MTX_UNLOCK(&conn->task_queue_mutex); if (lu_task != NULL) { if (lu_task->lu_cmd.W_bit) { /* write */ if (lu_task->req_transfer_out != 0) { /* error transfer */ lu_task->error = 1; lu_task->abort = 1; rc = pthread_cond_broadcast(&lu_task->trans_cond); if (rc != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); /* ignore error */ } } else { if (lu_task->req_execute) { conn->running_tasks--; if (conn->running_tasks == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "task cleanup finished\n"); break; } } /* ignore response */ rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); /* ignore error */ } } } else { /* read or no data */ /* ignore response */ rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); /* ignore error */ } } } else { ISTGT_ERRLOG("lu_task is NULL\n"); break; } } } istgt_clear_all_transfer_task(conn); #ifdef ISTGT_USE_KQUEUE close(kq); #endif /* ISTGT_USE_KQUEUE */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "waiting task end (%d) (left %d tasks)\n", conn->id, conn->running_tasks); } static void worker_cleanup(void *arg) { CONN_Ptr conn = (CONN_Ptr) arg; ISTGT_LU_Ptr lu; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "worker_cleanup\n"); ISTGT_WARNLOG("force cleanup execute\n"); /* cleanup */ pthread_mutex_unlock(&conn->task_queue_mutex); pthread_mutex_unlock(&conn->result_queue_mutex); if (conn->sess != NULL) { if (conn->sess->lu != NULL) { pthread_mutex_unlock(&conn->sess->lu->mutex); } pthread_mutex_unlock(&conn->sess->mutex); } if (conn->exec_lu_task != NULL) { conn->exec_lu_task->error = 1; pthread_cond_broadcast(&conn->exec_lu_task->trans_cond); pthread_mutex_unlock(&conn->exec_lu_task->trans_mutex); } pthread_mutex_unlock(&conn->wpdu_mutex); pthread_mutex_unlock(&conn->r2t_mutex); pthread_mutex_unlock(&conn->istgt->mutex); pthread_mutex_unlock(&g_conns_mutex); pthread_mutex_unlock(&g_last_tsih_mutex); conn->state = CONN_STATE_EXITING; if (conn->sess != NULL) { SESS_MTX_LOCK(conn); lu = conn->sess->lu; if (lu != NULL && lu->queue_depth != 0) { rc = istgt_lu_clear_task_IT(conn, lu); if (rc < 0) { ISTGT_ERRLOG("lu_clear_task_IT() failed\n"); } istgt_clear_all_transfer_task(conn); } SESS_MTX_UNLOCK(conn); } if (conn->pdu.copy_pdu == 0) { xfree(conn->pdu.ahs); conn->pdu.ahs = NULL; if (conn->pdu.data != conn->pdu.shortdata) { xfree(conn->pdu.data); } conn->pdu.data = NULL; } wait_all_task(conn); if (conn->use_sender) { pthread_cond_broadcast(&conn->result_queue_cond); pthread_join(conn->sender_thread, NULL); } close(conn->sock); #ifdef ISTGT_USE_KQUEUE close(conn->kq); conn->kq = -1; #endif /* ISTGT_USE_KQUEUE */ sleep(1); /* cleanup conn & sess */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "cancel cleanup LOCK\n"); MTX_LOCK(&g_conns_mutex); g_conns[conn->id] = NULL; istgt_remove_conn(conn); MTX_UNLOCK(&g_conns_mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "cancel cleanup UNLOCK\n"); return; } static void * sender(void *arg) { CONN_Ptr conn = (CONN_Ptr) arg; ISTGT_LU_TASK_Ptr lu_task; struct timespec abstime; time_t now; int rc; #ifdef HAVE_PTHREAD_SET_NAME_NP { char buf[MAX_TMPBUF]; snprintf(buf, sizeof buf, "sendthread #%d", conn->id); pthread_set_name_np(conn->sender_thread, buf); } #endif memset(&abstime, 0, sizeof abstime); /* handle DATA-IN/SCSI status */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sender loop start (%d)\n", conn->id); //MTX_LOCK(&conn->sender_mutex); while (1) { if (conn->state != CONN_STATE_RUNNING) { break; } MTX_LOCK(&conn->result_queue_mutex); lu_task = istgt_queue_dequeue(&conn->result_queue); if (lu_task == NULL) { now = time(NULL); abstime.tv_sec = now + conn->timeout; abstime.tv_nsec = 0; rc = pthread_cond_timedwait(&conn->result_queue_cond, &conn->result_queue_mutex, &abstime); if (rc == ETIMEDOUT) { /* nothing */ } lu_task = istgt_queue_dequeue(&conn->result_queue); if (lu_task == NULL) { MTX_UNLOCK(&conn->result_queue_mutex); continue; } } MTX_UNLOCK(&conn->result_queue_mutex); /* send all responses */ // MTX_LOCK(&conn->wpdu_mutex); do { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "task response CmdSN=%u\n", lu_task->lu_cmd.CmdSN); lu_task->lock = 1; if (lu_task->type == ISTGT_LU_TASK_RESPONSE) { /* send DATA-IN, SCSI status */ rc = istgt_iscsi_task_response(conn, lu_task); if (rc < 0) { lu_task->error = 1; ISTGT_ERRLOG( "iscsi_task_response() failed on %s(%s)\n", conn->target_port, conn->initiator_port); break; } rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); break; } } else if (lu_task->type == ISTGT_LU_TASK_REQPDU) { /* send PDU */ rc = istgt_iscsi_write_pdu_internal(lu_task->conn, lu_task->lu_cmd.pdu); if (rc < 0) { lu_task->error = 1; ISTGT_ERRLOG( "iscsi_write_pdu() failed on %s(%s)\n", lu_task->conn->target_port, lu_task->conn->initiator_port); break; } /* free allocated memory by caller */ xfree(lu_task); } else { ISTGT_ERRLOG("Unknown task type %x\n", lu_task->type); rc = -1; } // conn is running? if (conn->state != CONN_STATE_RUNNING) { //ISTGT_WARNLOG("exit thread\n"); break; } MTX_LOCK(&conn->result_queue_mutex); lu_task = istgt_queue_dequeue(&conn->result_queue); MTX_UNLOCK(&conn->result_queue_mutex); } while (lu_task != NULL); // MTX_UNLOCK(&conn->wpdu_mutex); } //MTX_UNLOCK(&conn->sender_mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sender loop ended (%d)\n", conn->id); return NULL; } static void * worker(void *arg) { CONN_Ptr conn = (CONN_Ptr) arg; ISTGT_LU_TASK_Ptr lu_task; ISTGT_LU_Ptr lu; ISCSI_PDU_Ptr pdu; sigset_t signew, sigold; #ifdef ISTGT_USE_KQUEUE int kq; struct kevent kev; struct timespec kev_timeout; #else struct pollfd fds[2]; int nopin_timer; #endif /* ISTGT_USE_KQUEUE */ int opcode; int rc; ISTGT_TRACELOG(ISTGT_TRACE_NET, "connect to %s:%s,%d\n", conn->portal.host, conn->portal.port, conn->portal.tag); #ifdef ISTGT_USE_KQUEUE kq = kqueue(); if (kq == -1) { ISTGT_ERRLOG("kqueue() failed\n"); return NULL; } conn->kq = kq; EV_SET(&kev, conn->sock, EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return NULL; } EV_SET(&kev, conn->task_pipe[0], EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return NULL; } EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return NULL; } EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return NULL; } #else memset(&fds, 0, sizeof fds); fds[0].fd = conn->sock; fds[0].events = POLLIN; fds[1].fd = conn->task_pipe[0]; fds[1].events = POLLIN; nopin_timer = conn->nopininterval; #endif /* ISTGT_USE_KQUEUE */ conn->pdu.ahs = NULL; conn->pdu.data = NULL; conn->pdu.copy_pdu = 0; conn->state = CONN_STATE_RUNNING; conn->exec_lu_task = NULL; lu_task = NULL; pthread_cleanup_push(worker_cleanup, conn); pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); conn->use_sender = 0; if (conn->istgt->swmode >= ISTGT_SWMODE_NORMAL) { /* create sender thread */ #ifdef ISTGT_STACKSIZE rc = pthread_create(&conn->sender_thread, &conn->istgt->attr, &sender, (void *)conn); #else rc = pthread_create(&conn->sender_thread, NULL, &sender, (void *)conn); #endif if (rc != 0) { ISTGT_ERRLOG("pthread_create() failed\n"); goto cleanup_exit; } conn->use_sender = 1; } conn->wsock = conn->sock; sigemptyset(&signew); sigemptyset(&sigold); sigaddset(&signew, ISTGT_SIGWAKEUP); pthread_sigmask(SIG_UNBLOCK, &signew, &sigold); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop start (%d)\n", conn->id); while (1) { /* check exit request */ if (conn->sess != NULL) { SESS_MTX_LOCK(conn); lu = conn->sess->lu; SESS_MTX_UNLOCK(conn); } else { lu = NULL; } if (lu != NULL) { if (istgt_lu_get_state(lu) != ISTGT_STATE_RUNNING) { conn->state = CONN_STATE_EXITING; break; } } else { if (istgt_get_state(conn->istgt) != ISTGT_STATE_RUNNING) { conn->state = CONN_STATE_EXITING; break; } } pthread_testcancel(); if (conn->state != CONN_STATE_RUNNING) { break; } #ifdef ISTGT_USE_KQUEUE ISTGT_TRACELOG(ISTGT_TRACE_NET, "kevent sock %d (timeout %dms)\n", conn->sock, conn->nopininterval); if (conn->nopininterval != 0) { kev_timeout.tv_sec = conn->nopininterval / 1000; kev_timeout.tv_nsec = (conn->nopininterval % 1000) * 1000000; } else { kev_timeout.tv_sec = DEFAULT_NOPININTERVAL; kev_timeout.tv_nsec = 0; } rc = kevent(kq, NULL, 0, &kev, 1, &kev_timeout); if (rc == -1 && errno == EINTR) { //ISTGT_ERRLOG("EINTR kevent\n"); continue; } if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); break; } if (rc == 0) { /* idle timeout, send diagnosis packet */ if (conn->nopininterval != 0) { rc = istgt_iscsi_send_nopin(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_send_nopin() failed\n"); break; } } continue; } if (kev.filter == EVFILT_SIGNAL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent SIGNAL\n"); if (kev.ident == SIGINT || kev.ident == SIGTERM) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent SIGNAL SIGINT/SIGTERM\n"); break; } continue; } #else //ISTGT_TRACELOG(ISTGT_TRACE_NET, "poll sock %d\n", conn->sock); rc = poll(fds, 2, POLLWAIT); if (rc == -1 && errno == EINTR) { //ISTGT_ERRLOG("EINTR poll\n"); continue; } if (rc == -1) { ISTGT_ERRLOG("poll() failed\n"); break; } if (rc == 0) { /* no fds */ if (nopin_timer > 0) { nopin_timer -= POLLWAIT; if (nopin_timer <= 0) { nopin_timer = conn->nopininterval; rc = istgt_iscsi_send_nopin(conn); if (rc < 0) { ISTGT_ERRLOG("iscsi_send_nopin() failed\n"); break; } } } continue; } nopin_timer = conn->nopininterval; #endif /* ISTGT_USE_KQUEUE */ /* on socket */ #ifdef ISTGT_USE_KQUEUE if (kev.ident == conn->sock) { if (kev.flags & (EV_EOF|EV_ERROR)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent EOF/ERROR\n"); break; } #else if (fds[0].revents & POLLHUP) { break; } if (fds[0].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ conn->pdu.copy_pdu = 0; rc = istgt_iscsi_read_pdu(conn, &conn->pdu); if (rc < 0) { ISTGT_ERRLOG("conn->state = %d\n", conn->state); if (conn->state != CONN_STATE_RUNNING) { if (errno == EINPROGRESS) { sleep(1); continue; } if (errno == ECONNRESET || errno == ETIMEDOUT) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "iscsi_read_pdu() RESET/TIMEOUT\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "iscsi_read_pdu() EOF\n"); } break; } ISTGT_ERRLOG("iscsi_read_pdu() failed\n"); break; } execute_pdu: opcode = BGET8W(&conn->pdu.bhs.opcode, 5, 6); #if 0 pthread_testcancel(); #endif if (conn->state != CONN_STATE_RUNNING) { break; } if (g_trace_flag) { if (conn->sess != NULL) { SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "isid=%"PRIx64", tsih=%u, cid=%u, op=%x\n", conn->sess->isid, conn->sess->tsih, conn->cid, opcode); SESS_MTX_UNLOCK(conn); } else { ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "isid=xxx, tsih=xxx, cid=%u, op=%x\n", conn->cid, opcode); } } rc = istgt_iscsi_execute(conn, &conn->pdu); if (rc < 0) { ISTGT_ERRLOG("iscsi_execute() failed on %s(%s)\n", conn->target_port, conn->initiator_port); break; } if (g_trace_flag) { if (conn->sess != NULL) { SESS_MTX_LOCK(conn); ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "isid=%"PRIx64", tsih=%u, cid=%u, op=%x complete\n", conn->sess->isid, conn->sess->tsih, conn->cid, opcode); SESS_MTX_UNLOCK(conn); } else { ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "isid=xxx, tsih=xxx, cid=%u, op=%x complete\n", conn->cid, opcode); } } if (opcode == ISCSI_OP_LOGOUT) { ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "logout received\n"); break; } if (conn->pdu.copy_pdu == 0) { xfree(conn->pdu.ahs); conn->pdu.ahs = NULL; if (conn->pdu.data != conn->pdu.shortdata) { xfree(conn->pdu.data); } conn->pdu.data = NULL; } /* execute pending PDUs */ pdu = istgt_queue_dequeue(&conn->pending_pdus); if (pdu != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "execute pending PDU\n"); rc = istgt_iscsi_copy_pdu(&conn->pdu, pdu); conn->pdu.copy_pdu = 0; xfree(pdu); goto execute_pdu; } #if 0 /* retry read/PDUs */ continue; #endif } /* execute on task queue */ #ifdef ISTGT_USE_KQUEUE if (kev.ident == conn->task_pipe[0]) { if (kev.flags & (EV_EOF|EV_ERROR)) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent EOF/ERROR\n"); break; } #else if (fds[1].revents & POLLHUP) { break; } if (fds[1].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ char tmp[1]; //ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Queue Task START\n"); rc = read(conn->task_pipe[0], tmp, 1); if (rc < 0 || rc == 0 || rc != 1) { ISTGT_ERRLOG("read() failed\n"); break; } /* DATA-IN/OUT */ MTX_LOCK(&conn->task_queue_mutex); rc = istgt_queue_count(&conn->task_queue); lu_task = istgt_queue_dequeue(&conn->task_queue); MTX_UNLOCK(&conn->task_queue_mutex); if (lu_task != NULL) { if (conn->exec_lu_task != NULL) { ISTGT_ERRLOG("task is overlapped (CmdSN=%u, %u)\n", conn->exec_lu_task->lu_cmd.CmdSN, lu_task->lu_cmd.CmdSN); break; } conn->exec_lu_task = lu_task; if (lu_task->lu_cmd.W_bit) { /* write */ if (lu_task->req_transfer_out == 0) { if (lu_task->req_execute) { if (conn->running_tasks > 0) { conn->running_tasks--; } else { ISTGT_ERRLOG("running no task\n"); } } rc = istgt_iscsi_task_response(conn, lu_task); if (rc < 0) { lu_task->error = 1; ISTGT_ERRLOG("iscsi_task_response() failed on %s(%s)\n", conn->target_port, conn->initiator_port); break; } rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); break; } lu_task = NULL; conn->exec_lu_task = NULL; } else { MTX_LOCK(&lu_task->trans_mutex); //ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, // "Task Write Trans START\n"); rc = istgt_iscsi_task_transfer_out(conn, lu_task); if (rc < 0) { lu_task->error = 1; MTX_UNLOCK(&lu_task->trans_mutex); ISTGT_ERRLOG("iscsi_task_transfer_out() failed on %s(%s)\n", conn->target_port, conn->initiator_port); break; } //ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, // "Task Write Trans END\n"); lu_task->req_transfer_out = 0; /* need response after execution */ lu_task->req_execute = 1; if (conn->use_sender == 0) { conn->running_tasks++; } MTX_UNLOCK(&lu_task->trans_mutex); rc = pthread_cond_broadcast(&lu_task->trans_cond); if (rc != 0) { //MTX_UNLOCK(&lu_task->trans_mutex); ISTGT_ERRLOG("cond_broadcast() failed\n"); break; } lu_task = NULL; conn->exec_lu_task = NULL; } } else { /* read or no data */ rc = istgt_iscsi_task_response(conn, lu_task); if (rc < 0) { lu_task->error = 1; ISTGT_ERRLOG("iscsi_task_response() failed on %s(%s)\n", conn->target_port, conn->initiator_port); break; } rc = istgt_lu_destroy_task(lu_task); if (rc < 0) { ISTGT_ERRLOG("lu_destroy_task() failed\n"); break; } lu_task = NULL; conn->exec_lu_task = NULL; } } /* XXX PDUs in DATA-OUT? */ pdu = istgt_queue_dequeue(&conn->pending_pdus); if (pdu != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "pending in task\n"); rc = istgt_iscsi_copy_pdu(&conn->pdu, pdu); conn->pdu.copy_pdu = 0; xfree(pdu); #ifdef ISTGT_USE_KQUEUE kev.ident = -1; #else fds[1].revents &= ~POLLIN; #endif /* ISTGT_USE_KQUEUE */ goto execute_pdu; } } } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop ended (%d)\n", conn->id); cleanup_exit: ; pthread_cleanup_pop(0); conn->state = CONN_STATE_EXITING; if (conn->sess != NULL) { SESS_MTX_LOCK(conn); lu = conn->sess->lu; if (lu != NULL && lu->queue_depth != 0) { rc = istgt_lu_clear_task_IT(conn, lu); if (rc < 0) { ISTGT_ERRLOG("lu_clear_task_IT() failed\n"); } istgt_clear_all_transfer_task(conn); } SESS_MTX_UNLOCK(conn); } if (conn->pdu.copy_pdu == 0) { xfree(conn->pdu.ahs); conn->pdu.ahs = NULL; if (conn->pdu.data != conn->pdu.shortdata) { xfree(conn->pdu.data); } conn->pdu.data = NULL; } wait_all_task(conn); if (conn->use_sender) { /* stop sender thread */ rc = pthread_cond_broadcast(&conn->result_queue_cond); if (rc != 0) { ISTGT_ERRLOG("cond_broadcast() failed\n"); /* ignore errors */ } rc = pthread_join(conn->sender_thread, NULL); if (rc != 0) { ISTGT_ERRLOG("pthread_join() failed\n"); /* ignore errors */ } } close(conn->sock); #ifdef ISTGT_USE_KQUEUE close(kq); conn->kq = -1; #endif /* ISTGT_USE_KQUEUE */ sleep(1); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "worker %d end\n", conn->id); /* cleanup conn & sess */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "cleanup LOCK\n"); MTX_LOCK(&g_conns_mutex); g_conns[conn->id] = NULL; istgt_remove_conn(conn); MTX_UNLOCK(&g_conns_mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "cleanup UNLOCK\n"); return NULL; } int istgt_create_conn(ISTGT_Ptr istgt, PORTAL_Ptr portal, int sock, struct sockaddr *sa, socklen_t salen) { char buf[MAX_TMPBUF]; CONN_Ptr conn; int rc; int i; conn = xmalloc(sizeof *conn); memset(conn, 0, sizeof *conn); conn->istgt = istgt; MTX_LOCK(&istgt->mutex); conn->timeout = istgt->timeout; conn->nopininterval = istgt->nopininterval; conn->nopininterval *= 1000; /* sec. to msec. */ conn->max_r2t = istgt->maxr2t; conn->TargetMaxRecvDataSegmentLength = istgt->MaxRecvDataSegmentLength; MTX_UNLOCK(&istgt->mutex); conn->MaxRecvDataSegmentLength = 8192; // RFC3720(12.12) if (conn->TargetMaxRecvDataSegmentLength < conn->MaxRecvDataSegmentLength) { conn->TargetMaxRecvDataSegmentLength = conn->MaxRecvDataSegmentLength; } conn->MaxOutstandingR2T = 1; conn->FirstBurstLength = DEFAULT_FIRSTBURSTLENGTH; conn->MaxBurstLength = DEFAULT_MAXBURSTLENGTH; conn->portal.label = xstrdup(portal->label); conn->portal.host = xstrdup(portal->host); conn->portal.port = xstrdup(portal->port); conn->portal.idx = portal->idx; conn->portal.tag = portal->tag; conn->portal.sock = -1; conn->sock = sock; conn->wsock = -1; #ifdef ISTGT_USE_KQUEUE conn->kq = -1; #endif /* ISTGT_USE_KQUEUE */ conn->use_sender = 0; conn->sess = NULL; conn->params = NULL; conn->state = CONN_STATE_INVALID; conn->exec_logout = 0; conn->max_pending = 0; conn->queue_depth = 0; conn->pending_r2t = 0; conn->header_digest = 0; conn->data_digest = 0; conn->full_feature = 0; conn->login_phase = ISCSI_LOGIN_PHASE_NONE; conn->auth.user = NULL; conn->auth.secret = NULL; conn->auth.muser = NULL; conn->auth.msecret = NULL; conn->authenticated = 0; conn->req_auth = 0; conn->req_mutual = 0; istgt_queue_init(&conn->pending_pdus); conn->r2t_tasks = xmalloc (sizeof *conn->r2t_tasks * (conn->max_r2t + 1)); for (i = 0; i < (conn->max_r2t + 1); i++) { conn->r2t_tasks[i] = NULL; } conn->task_pipe[0] = -1; conn->task_pipe[1] = -1; conn->max_task_queue = MAX_LU_QUEUE_DEPTH; istgt_queue_init(&conn->task_queue); istgt_queue_init(&conn->result_queue); conn->exec_lu_task = NULL; conn->running_tasks = 0; memset(conn->initiator_addr, 0, sizeof conn->initiator_addr); memset(conn->target_addr, 0, sizeof conn->target_addr); switch (sa->sa_family) { case AF_INET6: conn->initiator_family = AF_INET6; rc = istgt_getaddr(sock, conn->target_addr, sizeof conn->target_addr, conn->initiator_addr, sizeof conn->initiator_addr); if (rc < 0) { ISTGT_ERRLOG("istgt_getaddr() failed\n"); goto error_return; } break; case AF_INET: conn->initiator_family = AF_INET; rc = istgt_getaddr(sock, conn->target_addr, sizeof conn->target_addr, conn->initiator_addr, sizeof conn->initiator_addr); if (rc < 0) { ISTGT_ERRLOG("istgt_getaddr() failed\n"); goto error_return; } break; default: ISTGT_ERRLOG("unsupported family\n"); goto error_return; } printf("sock=%d, addr=%s, peer=%s\n", sock, conn->target_addr, conn->initiator_addr); /* wildcard? */ if (strcasecmp(conn->portal.host, "[::]") == 0 || strcasecmp(conn->portal.host, "[*]") == 0) { if (conn->initiator_family != AF_INET6) { ISTGT_ERRLOG("address family error\n"); goto error_return; } snprintf(buf, sizeof buf, "[%s]", conn->target_addr); xfree(conn->portal.host); conn->portal.host = xstrdup(buf); } else if (strcasecmp(conn->portal.host, "0.0.0.0") == 0 || strcasecmp(conn->portal.host, "*") == 0) { if (conn->initiator_family != AF_INET) { ISTGT_ERRLOG("address family error\n"); goto error_return; } snprintf(buf, sizeof buf, "%s", conn->target_addr); xfree(conn->portal.host); conn->portal.host = xstrdup(buf); } memset(conn->initiator_name, 0, sizeof conn->initiator_name); memset(conn->target_name, 0, sizeof conn->target_name); memset(conn->initiator_port, 0, sizeof conn->initiator_port); memset(conn->target_port, 0, sizeof conn->target_port); /* set timeout msec. */ rc = istgt_set_recvtimeout(conn->sock, conn->timeout * 1000); if (rc != 0) { ISTGT_ERRLOG("istgt_set_recvtimeo() failed\n"); goto error_return; } rc = istgt_set_sendtimeout(conn->sock, conn->timeout * 1000); if (rc != 0) { ISTGT_ERRLOG("istgt_set_sendtimeo() failed\n"); goto error_return; } rc = pipe(conn->task_pipe); if (rc != 0) { ISTGT_ERRLOG("pipe() failed\n"); conn->task_pipe[0] = -1; conn->task_pipe[1] = -1; goto error_return; } rc = pthread_mutex_init(&conn->task_queue_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); goto error_return; } rc = pthread_mutex_init(&conn->result_queue_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); goto error_return; } rc = pthread_cond_init(&conn->result_queue_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); goto error_return; } rc = pthread_mutex_init(&conn->wpdu_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); goto error_return; } rc = pthread_cond_init(&conn->wpdu_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); goto error_return; } rc = pthread_mutex_init(&conn->r2t_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); goto error_return; } rc = pthread_mutex_init(&conn->sender_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); goto error_return; } rc = pthread_cond_init(&conn->sender_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); goto error_return; } /* set default params */ rc = istgt_iscsi_conn_params_init(&conn->params); if (rc < 0) { ISTGT_ERRLOG("iscsi_conn_params_init() failed\n"); goto error_return; } /* replace with config value */ rc = istgt_iscsi_param_set_int(conn->params, "MaxRecvDataSegmentLength", conn->MaxRecvDataSegmentLength); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } conn->shortpdusize = ISTGT_SHORTPDUSIZE; conn->shortpdu = xmalloc(conn->shortpdusize); conn->iobufsize = ISTGT_IOBUFSIZE; conn->iobuf = xmalloc(conn->iobufsize); conn->snsbufsize = ISTGT_SNSBUFSIZE; conn->snsbuf = xmalloc(conn->snsbufsize); if (conn->MaxRecvDataSegmentLength < 8192) { conn->recvbufsize = 8192; conn->sendbufsize = 8192; } else { conn->recvbufsize = conn->MaxRecvDataSegmentLength; conn->sendbufsize = conn->MaxRecvDataSegmentLength; } conn->recvbuf = xmalloc(conn->recvbufsize); conn->sendbuf = xmalloc(conn->sendbufsize); conn->worksize = 0; conn->workbuf = NULL; /* register global */ rc = -1; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "register global LOCK\n"); MTX_LOCK(&g_conns_mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "register global LOCKED\n"); for (i = 0; i < g_nconns; i++) { if (g_conns[i] == NULL) { g_conns[i] = conn; conn->id = i; rc = 0; break; } } MTX_UNLOCK(&g_conns_mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "register global UNLOCK\n"); if (rc < 0) { ISTGT_ERRLOG("no free conn slot available\n"); error_return: if (conn->task_pipe[0] != -1) close(conn->task_pipe[0]); if (conn->task_pipe[1] != -1) close(conn->task_pipe[1]); istgt_iscsi_param_free(conn->params); istgt_queue_destroy(&conn->pending_pdus); istgt_queue_destroy(&conn->task_queue); xfree(conn->portal.label); xfree(conn->portal.host); xfree(conn->portal.port); xfree(conn->iobuf); xfree(conn->snsbuf); xfree(conn->recvbuf); xfree(conn->sendbuf); xfree(conn); return -1; } /* create new thread */ #ifdef ISTGT_STACKSIZE rc = pthread_create(&conn->thread, &istgt->attr, &worker, (void *)conn); #else rc = pthread_create(&conn->thread, NULL, &worker, (void *)conn); #endif /* ISTGT_STACKSIZE */ if (rc != 0) { ISTGT_ERRLOG("pthread_create() failed\n"); goto error_return; } rc = pthread_detach(conn->thread); if (rc != 0) { ISTGT_ERRLOG("pthread_detach() failed\n"); goto error_return; } #ifdef HAVE_PTHREAD_SET_NAME_NP snprintf(buf, sizeof buf, "connthread #%d", conn->id); pthread_set_name_np(conn->thread, buf); #endif return 0; } int istgt_create_sess(ISTGT_Ptr istgt, CONN_Ptr conn, ISTGT_LU_Ptr lu) { SESS_Ptr sess; int rc; sess = xmalloc(sizeof *sess); memset(sess, 0, sizeof *sess); /* configuration values */ MTX_LOCK(&istgt->mutex); if (lu != NULL) { MTX_LOCK(&lu->mutex); } sess->MaxConnections = istgt->MaxConnections; if (lu != NULL) { sess->MaxOutstandingR2T = lu->MaxOutstandingR2T; } else { sess->MaxOutstandingR2T = istgt->MaxOutstandingR2T; } #if 0 if (sess->MaxOutstandingR2T > conn->max_r2t) { if (conn->max_r2t > 0) { sess->MaxOutstandingR2T = conn->max_r2t; } else { sess->MaxOutstandingR2T = 1; } } #else if (sess->MaxOutstandingR2T < 1) { sess->MaxOutstandingR2T = 1; } /* limit up to MaxOutstandingR2T */ if (sess->MaxOutstandingR2T < conn->max_r2t) { conn->max_r2t = sess->MaxOutstandingR2T; } #endif if (lu != NULL) { sess->DefaultTime2Wait = lu->DefaultTime2Wait; sess->DefaultTime2Retain = lu->DefaultTime2Retain; sess->FirstBurstLength = lu->FirstBurstLength; sess->MaxBurstLength = lu->MaxBurstLength; conn->MaxRecvDataSegmentLength = lu->MaxRecvDataSegmentLength; sess->InitialR2T = lu->InitialR2T; sess->ImmediateData = lu->ImmediateData; sess->DataPDUInOrder = lu->DataPDUInOrder; sess->DataSequenceInOrder = lu->DataSequenceInOrder; sess->ErrorRecoveryLevel = lu->ErrorRecoveryLevel; } else { sess->DefaultTime2Wait = istgt->DefaultTime2Wait; sess->DefaultTime2Retain = istgt->DefaultTime2Retain; sess->FirstBurstLength = istgt->FirstBurstLength; sess->MaxBurstLength = istgt->MaxBurstLength; conn->MaxRecvDataSegmentLength = istgt->MaxRecvDataSegmentLength; sess->InitialR2T = istgt->InitialR2T; sess->ImmediateData = istgt->ImmediateData; sess->DataPDUInOrder = istgt->DataPDUInOrder; sess->DataSequenceInOrder = istgt->DataSequenceInOrder; sess->ErrorRecoveryLevel = istgt->ErrorRecoveryLevel; } if (lu != NULL) { MTX_UNLOCK(&lu->mutex); } MTX_UNLOCK(&istgt->mutex); sess->initiator_port = xstrdup(conn->initiator_port); sess->target_name = xstrdup(conn->target_name); sess->tag = conn->portal.tag; sess->max_conns = sess->MaxConnections; sess->conns = xmalloc(sizeof *sess->conns * sess->max_conns); memset(sess->conns, 0, sizeof *sess->conns * sess->max_conns); sess->connections = 0; sess->conns[sess->connections] = conn; sess->connections++; sess->req_mcs_cond = 0; sess->params = NULL; sess->lu = NULL; sess->isid = 0; sess->tsih = 0; sess->initial_r2t = 0; sess->immediate_data = 0; rc = pthread_mutex_init(&sess->mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); error_return: istgt_iscsi_param_free(sess->params); xfree(sess->initiator_port); xfree(sess->target_name); xfree(sess->conns); xfree(sess); conn->sess = NULL; return -1; } rc = pthread_cond_init(&sess->mcs_cond, NULL); if (rc != 0) { ISTGT_ERRLOG("cond_init() failed\n"); goto error_return; } /* set default params */ rc = istgt_iscsi_sess_params_init(&sess->params); if (rc < 0) { ISTGT_ERRLOG("iscsi_sess_params_init() failed\n"); goto error_return; } /* replace with config value */ rc = istgt_iscsi_param_set_int(sess->params, "MaxConnections", sess->MaxConnections); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } rc = istgt_iscsi_param_set_int(sess->params, "MaxOutstandingR2T", sess->MaxOutstandingR2T); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } rc = istgt_iscsi_param_set_int(sess->params, "DefaultTime2Wait", sess->DefaultTime2Wait); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } rc = istgt_iscsi_param_set_int(sess->params, "DefaultTime2Retain", sess->DefaultTime2Retain); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } rc = istgt_iscsi_param_set_int(sess->params, "FirstBurstLength", sess->FirstBurstLength); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } rc = istgt_iscsi_param_set_int(sess->params, "MaxBurstLength", sess->MaxBurstLength); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } rc = istgt_iscsi_param_set(sess->params, "InitialR2T", sess->InitialR2T ? "Yes" : "No"); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set() failed\n"); goto error_return; } rc = istgt_iscsi_param_set(sess->params, "ImmediateData", sess->ImmediateData ? "Yes" : "No"); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set() failed\n"); goto error_return; } rc = istgt_iscsi_param_set(sess->params, "DataPDUInOrder", sess->DataPDUInOrder ? "Yes" : "No"); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set() failed\n"); goto error_return; } rc = istgt_iscsi_param_set(sess->params, "DataSequenceInOrder", sess->DataSequenceInOrder ? "Yes" : "No"); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set() failed\n"); goto error_return; } rc = istgt_iscsi_param_set_int(sess->params, "ErrorRecoveryLevel", sess->ErrorRecoveryLevel); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } /* realloc buffer */ rc = istgt_iscsi_param_set_int(conn->params, "MaxRecvDataSegmentLength", conn->MaxRecvDataSegmentLength); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_set_int() failed\n"); goto error_return; } if (conn->MaxRecvDataSegmentLength != conn->recvbufsize) { xfree(conn->recvbuf); xfree(conn->sendbuf); if (conn->MaxRecvDataSegmentLength < 8192) { conn->recvbufsize = 8192; conn->sendbufsize = 8192; } else { conn->recvbufsize = conn->MaxRecvDataSegmentLength; conn->sendbufsize = conn->MaxRecvDataSegmentLength; } conn->recvbuf = xmalloc(conn->recvbufsize); conn->sendbuf = xmalloc(conn->sendbufsize); } /* sess for first connection of session */ conn->sess = sess; return 0; } static int istgt_append_sess(CONN_Ptr conn, uint64_t isid, uint16_t tsih, uint16_t cid) { SESS_Ptr sess; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_ISCSI, "append session: isid=%"PRIx64", tsih=%u, cid=%u\n", isid, tsih, cid); sess = NULL; rc = -1; MTX_LOCK(&g_conns_mutex); for (i = 0; i < g_nconns; i++) { if (g_conns[i] == NULL || g_conns[i]->sess == NULL) continue; sess = g_conns[i]->sess; MTX_LOCK(&sess->mutex); if (conn->portal.tag == sess->tag && strcasecmp(conn->initiator_port, sess->initiator_port) == 0 && strcasecmp(conn->target_name, sess->target_name) == 0 && (isid == sess->isid && tsih == sess->tsih)) { /* match tag and initiator port and target */ rc = 0; break; } MTX_UNLOCK(&sess->mutex); } if (rc < 0) { /* no match */ MTX_UNLOCK(&g_conns_mutex); ISTGT_ERRLOG("no MCS session for isid=%"PRIx64", tsih=%d, cid=%d\n", isid, tsih, cid); return -1; } /* sess is LOCK by loop */ if (sess->connections >= sess->max_conns || sess->connections >= sess->MaxConnections) { /* no slot for connection */ MTX_UNLOCK(&sess->mutex); MTX_UNLOCK(&g_conns_mutex); ISTGT_ERRLOG("too many connections for isid=%"PRIx64 ", tsih=%d, cid=%d\n", isid, tsih, cid); return -1; } printf("Connections(tsih %d): %d\n", sess->tsih, sess->connections); conn->sess = sess; sess->conns[sess->connections] = conn; sess->connections++; MTX_UNLOCK(&sess->mutex); MTX_UNLOCK(&g_conns_mutex); return 0; } static void istgt_free_sess(SESS_Ptr sess) { if (sess == NULL) return; (void) pthread_mutex_destroy(&sess->mutex); (void) pthread_cond_destroy(&sess->mcs_cond); istgt_iscsi_param_free(sess->params); xfree(sess->initiator_port); xfree(sess->target_name); xfree(sess->conns); xfree(sess); } static void istgt_free_conn(CONN_Ptr conn) { if (conn == NULL) return; if (conn->task_pipe[0] != -1) close(conn->task_pipe[0]); if (conn->task_pipe[1] != -1) close(conn->task_pipe[1]); (void) pthread_mutex_destroy(&conn->task_queue_mutex); (void) pthread_mutex_destroy(&conn->result_queue_mutex); (void) pthread_cond_destroy(&conn->result_queue_cond); (void) pthread_mutex_destroy(&conn->wpdu_mutex); (void) pthread_cond_destroy(&conn->wpdu_cond); (void) pthread_mutex_destroy(&conn->r2t_mutex); (void) pthread_mutex_destroy(&conn->sender_mutex); (void) pthread_cond_destroy(&conn->sender_cond); istgt_iscsi_param_free(conn->params); istgt_queue_destroy(&conn->pending_pdus); istgt_queue_destroy(&conn->task_queue); istgt_queue_destroy(&conn->result_queue); xfree(conn->r2t_tasks); xfree(conn->portal.label); xfree(conn->portal.host); xfree(conn->portal.port); xfree(conn->auth.user); xfree(conn->auth.secret); xfree(conn->auth.muser); xfree(conn->auth.msecret); xfree(conn->shortpdu); xfree(conn->iobuf); xfree(conn->snsbuf); xfree(conn->recvbuf); xfree(conn->sendbuf); xfree(conn->workbuf); xfree(conn); } static void istgt_remove_conn(CONN_Ptr conn) { SESS_Ptr sess; int idx; int i, j; idx = -1; sess = conn->sess; conn->sess = NULL; if (sess == NULL) { istgt_free_conn(conn); return; } MTX_LOCK(&sess->mutex); for (i = 0; i < sess->connections; i++) { if (sess->conns[i] == conn) { idx = i; break; } } if (sess->connections < 1) { ISTGT_ERRLOG("zero connection\n"); sess->connections = 0; } else { if (idx < 0) { ISTGT_ERRLOG("remove conn not found\n"); } else { for (j = idx; j < sess->connections - 1; j++) { sess->conns[j] = sess->conns[j + 1]; } sess->conns[sess->connections - 1] = NULL; } sess->connections--; } printf("Connections(tsih %d): %d\n", sess->tsih, sess->connections); if (sess->connections == 1) { /* cleanup for multiple connecsions */ MTX_UNLOCK(&sess->mutex); } else if (sess->connections == 0) { /* cleanup last connection */ MTX_UNLOCK(&sess->mutex); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "cleanup last conn free tsih\n"); istgt_lu_free_tsih(sess->lu, sess->tsih, conn->initiator_port); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "cleanup last conn free sess\n"); istgt_free_sess(sess); } else { MTX_UNLOCK(&sess->mutex); } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "cleanup free conn\n"); istgt_free_conn(conn); } static int istgt_iscsi_drop_all_conns(CONN_Ptr conn) { CONN_Ptr xconn; int max_conns; int num; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_iscsi_drop_all_conns\n"); printf("drop all connections %s by %s\n", conn->target_name, conn->initiator_name); MTX_LOCK(&conn->istgt->mutex); max_conns = conn->istgt->MaxConnections; MTX_UNLOCK(&conn->istgt->mutex); num = 0; MTX_LOCK(&g_conns_mutex); for (i = 0; i < g_nconns; i++) { xconn = g_conns[i]; if (xconn == NULL) continue; if (xconn == conn) continue; if (strcasecmp(conn->initiator_name, xconn->initiator_name) != 0) { continue; } if (strcasecmp(conn->target_name, xconn->target_name) == 0) { if (xconn->sess != NULL) { printf("exiting conn by %s(%s), TSIH=%u, CID=%u\n", xconn->initiator_name, xconn->initiator_addr, xconn->sess->tsih, xconn->cid); } else { printf("exiting conn by %s(%s), TSIH=xx, CID=%u\n", xconn->initiator_name, xconn->initiator_addr, xconn->cid); } xconn->state = CONN_STATE_EXITING; num++; } } istgt_yield(); sleep(1); if (num > max_conns + 1) { printf("try pthread_cancel\n"); for (i = 0; i < g_nconns; i++) { xconn = g_conns[i]; if (xconn == NULL) continue; if (xconn == conn) continue; if (strcasecmp(conn->initiator_port, xconn->initiator_port) != 0) { continue; } if (strcasecmp(conn->target_name, xconn->target_name) == 0) { if (xconn->sess != NULL) { printf("exiting conn by %s(%s), TSIH=%u, CID=%u\n", xconn->initiator_port, xconn->initiator_addr, xconn->sess->tsih, xconn->cid); } else { printf("exiting conn by %s(%s), TSIH=xx, CID=%u\n", xconn->initiator_port, xconn->initiator_addr, xconn->cid); } rc = pthread_cancel(xconn->thread); if (rc != 0) { ISTGT_ERRLOG("pthread_cancel() failed rc=%d\n", rc); } } } } MTX_UNLOCK(&g_conns_mutex); if (num != 0) { printf("exiting %d conns\n", num); } return 0; } static int istgt_iscsi_drop_old_conns(CONN_Ptr conn) { CONN_Ptr xconn; int max_conns; int num; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_iscsi_drop_old_conns\n"); printf("drop old connections %s by %s\n", conn->target_name, conn->initiator_port); MTX_LOCK(&conn->istgt->mutex); max_conns = conn->istgt->MaxConnections; MTX_UNLOCK(&conn->istgt->mutex); num = 0; MTX_LOCK(&g_conns_mutex); for (i = 0; i < g_nconns; i++) { xconn = g_conns[i]; if (xconn == NULL) continue; if (xconn == conn) continue; if (strcasecmp(conn->initiator_port, xconn->initiator_port) != 0) { continue; } if (strcasecmp(conn->target_name, xconn->target_name) == 0) { if (xconn->sess != NULL) { printf("exiting conn by %s(%s), TSIH=%u, CID=%u\n", xconn->initiator_port, xconn->initiator_addr, xconn->sess->tsih, xconn->cid); } else { printf("exiting conn by %s(%s), TSIH=xx, CID=%u\n", xconn->initiator_port, xconn->initiator_addr, xconn->cid); } xconn->state = CONN_STATE_EXITING; num++; } } istgt_yield(); sleep(1); if (num > max_conns + 1) { printf("try pthread_cancel\n"); for (i = 0; i < g_nconns; i++) { xconn = g_conns[i]; if (xconn == NULL) continue; if (xconn == conn) continue; if (strcasecmp(conn->initiator_port, xconn->initiator_port) != 0) { continue; } if (strcasecmp(conn->target_name, xconn->target_name) == 0) { if (xconn->sess != NULL) { printf("exiting conn by %s(%s), TSIH=%u, CID=%u\n", xconn->initiator_port, xconn->initiator_addr, xconn->sess->tsih, xconn->cid); } else { printf("exiting conn by %s(%s), TSIH=xx, CID=%u\n", xconn->initiator_port, xconn->initiator_addr, xconn->cid); } rc = pthread_cancel(xconn->thread); if (rc != 0) { ISTGT_ERRLOG("pthread_cancel() failed rc=%d\n", rc); } } } } MTX_UNLOCK(&g_conns_mutex); if (num != 0) { printf("exiting %d conns\n", num); } return 0; } void istgt_lock_gconns(void) { MTX_LOCK(&g_conns_mutex); } void istgt_unlock_gconns(void) { MTX_UNLOCK(&g_conns_mutex); } int istgt_get_gnconns(void) { return g_nconns; } CONN_Ptr istgt_get_gconn(int idx) { if (idx >= g_nconns) return NULL; return g_conns[idx]; } int istgt_get_active_conns(void) { CONN_Ptr conn; int num = 0; int i; MTX_LOCK(&g_conns_mutex); for (i = 0; i < g_nconns; i++) { conn = g_conns[i]; if (conn == NULL) continue; num++; } MTX_UNLOCK(&g_conns_mutex); return num; } CONN_Ptr istgt_find_conn(const char *initiator_port, const char *target_name, uint16_t tsih) { CONN_Ptr conn; SESS_Ptr sess; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "initiator_port=%s, target=%s, TSIH=%u", initiator_port, target_name, tsih); sess = NULL; rc = -1; //MTX_LOCK(&g_conns_mutex); for (i = 0; i < g_nconns; i++) { conn = g_conns[i]; if (conn == NULL || conn->sess == NULL) continue; sess = conn->sess; MTX_LOCK(&sess->mutex); if (strcasecmp(initiator_port, sess->initiator_port) == 0 && strcasecmp(target_name, sess->target_name) == 0 && (tsih == sess->tsih)) { /* match initiator port and target */ rc = 0; break; } MTX_UNLOCK(&sess->mutex); } if (rc < 0) { //MTX_UNLOCK(&g_conns_mutex); return NULL; } MTX_UNLOCK(&sess->mutex); //MTX_UNLOCK(&g_conns_mutex); return conn; } int istgt_iscsi_init(ISTGT_Ptr istgt) { CF_SECTION *sp; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_iscsi_init\n"); sp = istgt_find_cf_section(istgt->config, "Global"); if (sp == NULL) { ISTGT_ERRLOG("find_cf_section failed()\n"); return -1; } rc = pthread_mutex_init(&g_conns_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); return -1; } rc = pthread_mutex_init(&g_last_tsih_mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); return -1; } g_nconns = MAX_LOGICAL_UNIT * istgt->MaxSessions * istgt->MaxConnections; g_nconns += MAX_LOGICAL_UNIT * istgt->MaxConnections; g_conns = xmalloc(sizeof *g_conns * g_nconns); for (i = 0; i < g_nconns; i++) { g_conns[i] = NULL; } g_last_tsih = 0; return 0; } int istgt_iscsi_shutdown(ISTGT_Ptr istgt) { CONN_Ptr conn; int retry = 10; int num; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_iscsi_shutdown\n"); num = 0; MTX_LOCK(&g_conns_mutex); for (i = 0; i < g_nconns; i++) { conn = g_conns[i]; if (conn == NULL) continue; conn->state = CONN_STATE_EXITING; num++; } MTX_UNLOCK(&g_conns_mutex); if (num != 0) { /* check threads */ while (retry > 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "check thread retry=%d\n", retry); sleep(1); num = 0; MTX_LOCK(&g_conns_mutex); for (i = 0; i < g_nconns; i++) { conn = g_conns[i]; if (conn == NULL) continue; num++; } MTX_UNLOCK(&g_conns_mutex); if (num == 0) break; retry--; } } rc = pthread_mutex_destroy(&g_last_tsih_mutex); if (rc != 0) { ISTGT_ERRLOG("mutex_destroy() failed\n"); return -1; } rc = pthread_mutex_destroy(&g_conns_mutex); if (rc != 0) { ISTGT_ERRLOG("mutex_destroy() failed\n"); return -1; } return 0; } istgt-20111008/src/istgt_crc32c.h000644 000000 000000 00000003723 11317704406 016442 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_CRC32C_H #define ISTGT_CRC32C_H #include #include #include #define ISTGT_USE_CRC32C_TABLE #define ISTGT_CRC32C_INITIAL 0xffffffffUL #define ISTGT_CRC32C_XOR 0xffffffffUL #define ISTGT_CRC32C_POLYNOMIAL 0x1edc6f41UL void istgt_init_crc32c_table(void); uint32_t istgt_update_crc32c(const uint8_t *buf, size_t len, uint32_t crc); uint32_t istgt_fixup_crc32c(size_t total, uint32_t crc); uint32_t istgt_crc32c(const uint8_t *buf, size_t len); uint32_t istgt_iovec_crc32c(const struct iovec *iovp, int iovc, uint32_t offset, uint32_t len); #endif /* ISTGT_CRC32C_H */ istgt-20111008/src/Makefile.in000644 000000 000000 00000006645 11627501424 016052 0ustar00rootwheel000000 000000 ######################################################################### # Makefile for istgt ######################################################################### top_srcdir = @top_srcdir@ srcdir = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ datadir = @datarootdir@ libexecdir = @libexecdir@ CC = @CC@ CFLAGS = @CFLAGS@ CPPFLAGS = @CPPFLAGS@ -I$(top_srcdir) -I$(srcdir) LDFLAGS = @LDFLAGS@ DEFS = @DEFS@ LIBS = @LIBS@ INSTALL = @INSTALL@ RANLIB = @RANLIB@ MKDEP = @MKDEP@ CFLAGS += -DDEBUG CFLAGS += -fno-strict-aliasing -Wstrict-aliasing CFLAGS += -Wbad-function-cast -Wcast-align CFLAGS += -Wcast-qual -Wchar-subscripts -Winline CFLAGS += -Wmissing-prototypes -Wnested-externs -Wpointer-arith CFLAGS += -Wredundant-decls -Wshadow -Wstrict-prototypes -Wwrite-strings source = istgt.c istgt_iscsi.c istgt_iscsi_param.c \ istgt_lu.c istgt_lu_disk.c istgt_lu_dvd.c istgt_lu_tape.c \ istgt_lu_pass.c istgt_lu_ctl.c \ istgt_log.c istgt_conf.c istgt_sock.c istgt_misc.c \ istgt_queue.c istgt_crc32c.c istgt_md5.c header = istgt_ver.h istgt.h istgt_iscsi.h istgt_iscsi_param.h \ istgt_scsi.h istgt_proto.h istgt_lu.h \ istgt_log.h istgt_conf.h istgt_sock.h \ istgt_misc.h istgt_queue.h istgt_crc32c.h istgt_md5.h document = sample = ctl_source = istgtcontrol.c istgt_conf.c istgt_sock.c istgt_misc.c \ istgt_md5.c ctl_header = istgt_ver.h istgt_conf.h istgt_sock.h istgt_misc.h \ istgt_md5.h ISTGT = $(source:.c=.o) ISTGTCONTROL = $(ctl_source:.c=.o) PACKAGE_NAME = @PACKAGE_NAME@ PACKAGE_STRING = @PACKAGE_STRING@ PACKAGE_TARNAME = @PACKAGE_TARNAME@ PACKAGE_VERSION = @PACKAGE_VERSION@ VER_H = istgt_ver.h DISTBASE = istgt DISTVER = `sed -e '/ISTGT_VERSION/!d' -e 's/[^0-9.]*\([0-9.a-z]*\).*/\1/' $(VER_H)` DISTEXTVER = `sed -e '/ISTGT_EXTRA_VERSION/!d' -e 's/[^0-9.]*\([0-9.a-z]*\).*/\1/' $(VER_H)` #DISTDIR = $(PACKAGE_NAME)-$(PACKAGE_VERSION) #DISTDIR = $(DISTBASE)-$(DISTVER)-$(DISTEXTVER) #DISTDIR = $(DISTBASE)-$(DISTEXTVER) DISTDIR = $(top_srcdir)/`cat $(top_srcdir)/distdir` DISTNAME = $(DISTDIR).tar.gz DISTFILES = Makefile.in config.h.in build.h.in \ $(header) $(source) $(ctl_header) $(ctl_source) \ $(document) $(sample) ######################################################################### .SUFFIXES: .c .o .c.o: $(CC) $(DEFS) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< all: stamp-depend config.h istgt istgtcontrol istgt: $(ISTGT) $(CC) $(LDFLAGS) $(LIBS) -o $@ $(ISTGT) istgtcontrol: $(ISTGTCONTROL) $(CC) $(LDFLAGS) $(LIBS) -o $@ $(ISTGTCONTROL) install: install-dirs install -c -m 0755 istgt $(DESTDIR)$(bindir) install -c -m 0755 istgtcontrol $(DESTDIR)$(bindir) install-dirs: mkdir -p $(DESTDIR)$(bindir) dist: $(DISTFILES) mkdir $(DISTDIR)/$(subdir) cd $(srcdir); thisdir=`pwd`; \ for file in `echo $(DISTFILES) | sort`; do \ cp -p $$thisdir/$$file $(DISTDIR)/$(subdir); \ done depend: if [ "x$(MKDEP)" != "x" ]; then \ $(MKDEP) -MM $(DEFS) $(CFLAGS) $(CPPFLAGS) $(source); \ fi touch stamp-depend clean: -rm -f a.out *.o *.core -rm -f *~ -rm -f istgt istgtcontrol distclean: clean -rm -f stamp-depend .depend -rm -f stamp-h.in -rm -f Makefile config.status config.cache config.log config.h -rm -f build.h ######################################################################### stamp-depend: Makefile $(MAKE) depend ######################################################################### istgt-20111008/src/istgt_misc.c000644 000000 000000 00000025332 11415046013 016301 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_LIBPTHREAD #include #endif #ifdef HAVE_SCHED #include #endif #include "istgt.h" #include "istgt_misc.h" static void fatal(const char *format, ...) { char buf[MAX_TMPBUF]; va_list ap; va_start(ap, format); vsnprintf(buf, sizeof buf, format, ap); fprintf(stderr, "%s", buf); syslog(LOG_ERR, "%s", buf); va_end(ap); exit(EXIT_FAILURE); } void * xmalloc(size_t size) { void *p; if (size < 1) size = 1; p = malloc(size); if (p == NULL) fatal("no memory\n"); return p; } void * xrealloc(void *p, size_t size) { if (size < 1) size = 1; if (p == NULL) { p = malloc(size); } else { p = realloc(p, size); } if (p == NULL) fatal("no memory\n"); return p; } void xfree(void *p) { if (p == NULL) return; free(p); } char * xstrdup(const char *s) { char *p; size_t size; if (s == NULL) return NULL; size = strlen(s) + 1; p = xmalloc(size); memcpy(p, s, size - 1); p[size - 1] = '\0'; return p; } char * strlwr(char *s) { char *p; if (s == NULL) return NULL; p = s; while (*p != '\0') { *p = tolower((int) *p); p++; } return s; } char * strupr(char *s) { char *p; if (s == NULL) return NULL; p = s; while (*p != '\0') { *p = toupper((int) *p); p++; } return s; } char * strsepq(char **stringp, const char *delim) { char *p, *q, *r; int quoted = 0, bslash = 0; p = *stringp; if (p == NULL) return NULL; r = q = p; while (*q != '\0' && *q != '\n') { /* eat quoted characters */ if (bslash) { bslash = 0; *r++ = *q++; continue; } else if (quoted) { if (quoted == '"' && *q == '\\') { bslash = 1; q++; continue; } else if (*q == quoted) { quoted = 0; q++; continue; } *r++ = *q++; continue; } else if (*q == '\\') { bslash = 1; q++; continue; } else if (*q == '"' || *q == '\'') { quoted = *q; q++; continue; } /* separator? */ if (strchr(delim, (int) *q) == NULL) { *r++ = *q++; continue; } /* new string */ q++; break; } *r = '\0'; /* skip tailer */ while (*q != '\0' && strchr(delim, (int) *q) != NULL) { q++; } if (*q != '\0') { *stringp = q; } else { *stringp = NULL; } return p; } char * trim_string(char *s) { char *p, *q; if (s == NULL) return NULL; /* remove header */ p = s; while (*p != '\0' && isspace((int) *p)) { p++; } /* remove tailer */ q = p + strlen(p); while (q - 1 >= p && isspace((int) *(q - 1))) { q--; *q = '\0'; } /* if remove header, move */ if (p != s) { q = s; while (*p != '\0') { *q++ = *p++; } } return s; } char * escape_string(const char *s) { const char *p; char *q, *r; size_t size; if (s == NULL) return NULL; p = s; size = 0; while (*p != '\0') { if (*p == '"' || *p == '\\' || *p == '\'') { size += 2; } else { size++; } p++; } p = s; r = q = xmalloc(size + 1); while (*p != '\0') { if (*p == '"' || *p == '\\' || *p == '\'') { *q++ = '\\'; *q++ = *p++; } else { *q++ = *p++; } } *q++ = '\0'; return r; } /* LBA = (M * 60 + S) * 75 + F - 150 */ uint32_t istgt_msf2lba(uint32_t msf) { uint32_t lba; lba = ((msf >> 16) & 0xff) * 60 * 75; lba += ((msf >> 8) & 0xff) * 75; lba += msf & 0xff; lba -= 150; return lba; } uint32_t istgt_lba2msf(uint32_t lba) { uint32_t m, s, f; lba += 150; m = (lba / 75) / 60; s = (lba / 75) % 60; f = lba % 75; return ((m << 16) | (s << 8) | f); } uint8_t istgt_dget8(const uint8_t *data) { uint8_t value; value = (data[0] & 0xffU) << 0; return value; } void istgt_dset8(uint8_t *data, uint32_t value) { data[0] = (value >> 0) & 0xffU; } uint16_t istgt_dget16(const uint8_t *data) { uint16_t value; value = (data[0] & 0xffU) << 8; value |= (data[1] & 0xffU) << 0; return value; } void istgt_dset16(uint8_t *data, uint32_t value) { data[0] = (value >> 8) & 0xffU; data[1] = (value >> 0) & 0xffU; } uint32_t istgt_dget24(const uint8_t *data) { uint32_t value; value = (data[0] & 0xffU) << 16; value |= (data[1] & 0xffU) << 8; value |= (data[2] & 0xffU) << 0; return value; } void istgt_dset24(uint8_t *data, uint32_t value) { data[0] = (value >> 16) & 0xffU; data[1] = (value >> 8) & 0xffU; data[2] = (value >> 0) & 0xffU; } uint32_t istgt_dget32(const uint8_t *data) { uint32_t value; value = (data[0] & 0xffU) << 24; value |= (data[1] & 0xffU) << 16; value |= (data[2] & 0xffU) << 8; value |= (data[3] & 0xffU) << 0; return value; } void istgt_dset32(uint8_t *data, uint32_t value) { data[0] = (value >> 24) & 0xffU; data[1] = (value >> 16) & 0xffU; data[2] = (value >> 8) & 0xffU; data[3] = (value >> 0) & 0xffU; } uint64_t istgt_dget48(const uint8_t *data) { uint64_t value; value = (data[0] & 0xffULL) << 40; value |= (data[1] & 0xffULL) << 32; value |= (data[2] & 0xffULL) << 24; value |= (data[3] & 0xffULL) << 16; value |= (data[4] & 0xffULL) << 8; value |= (data[5] & 0xffULL) << 0; return value; } void istgt_dset48(uint8_t *data, uint64_t value) { data[0] = (value >> 40) & 0xffULL; data[1] = (value >> 32) & 0xffULL; data[2] = (value >> 24) & 0xffULL; data[3] = (value >> 16) & 0xffULL; data[4] = (value >> 8) & 0xffULL; data[5] = (value >> 0) & 0xffULL; } uint64_t istgt_dget64(const uint8_t *data) { uint64_t value; value = (data[0] & 0xffULL) << 56; value |= (data[1] & 0xffULL) << 48; value |= (data[2] & 0xffULL) << 40; value |= (data[3] & 0xffULL) << 32; value |= (data[4] & 0xffULL) << 24; value |= (data[5] & 0xffULL) << 16; value |= (data[6] & 0xffULL) << 8; value |= (data[7] & 0xffULL) << 0; return value; } void istgt_dset64(uint8_t *data, uint64_t value) { data[0] = (value >> 56) & 0xffULL; data[1] = (value >> 48) & 0xffULL; data[2] = (value >> 40) & 0xffULL; data[3] = (value >> 32) & 0xffULL; data[4] = (value >> 24) & 0xffULL; data[5] = (value >> 16) & 0xffULL; data[6] = (value >> 8) & 0xffULL; data[7] = (value >> 0) & 0xffULL; } void istgt_dump(const char *label, const uint8_t *buf, size_t len) { istgt_fdump(stdout, label, buf, len); } void istgt_fdump(FILE *fp, const char *label, const uint8_t *buf, size_t len) { char tmpbuf[MAX_TMPBUF]; char buf8[8+1]; int total; int i; fprintf(fp, "%s\n", label); memset(buf8, 0, sizeof buf8); total = 0; for (i = 0; i < len; i++) { if (i != 0 && i % 8 == 0) { total += snprintf(tmpbuf + total, sizeof tmpbuf - total, "%s", buf8); fprintf(fp, "%s\n", tmpbuf); total = 0; } total += snprintf(tmpbuf + total, sizeof tmpbuf - total, "%2.2x ", buf[i] & 0xff); buf8[i % 8] = isprint(buf[i]) ? buf[i] : '.'; } for ( ; i % 8 != 0; i++) { total += snprintf(tmpbuf + total, sizeof tmpbuf - total, " "); buf8[i % 8] = ' '; } total += snprintf(tmpbuf + total, sizeof tmpbuf - total, "%s", buf8); fprintf(fp, "%s\n", tmpbuf); fflush(fp); } #ifndef HAVE_SRANDOMDEV #include void srandomdev(void) { unsigned long seed; time_t now; pid_t pid; pid = getpid(); now = time(NULL); seed = pid ^ now; srandom(seed); } #endif /* HAVE_SRANDOMDEV */ #ifndef HAVE_ARC4RANDOM static int istgt_arc4random_initialized = 0; uint32_t arc4random(void) { uint32_t r; uint32_t r1, r2; if (!istgt_arc4random_initialized) { srandomdev(); istgt_arc4random_initialized = 1; } r1 = (uint32_t) (random() & 0xffff); r2 = (uint32_t) (random() & 0xffff); r = (r1 << 16) | r2; return r; } #endif /* HAVE_ARC4RANDOM */ void istgt_gen_random(uint8_t *buf, size_t len) { #ifdef USE_RANDOM long l; int i; srandomdev(); for (i = 0; i < len; i++) { l = random(); buf[i] = (uint8_t) l; } #else uint32_t r; int i; for (i = 0; i < len; i++) { r = arc4random(); buf[i] = (uint8_t) r; } #endif /* USE_RANDOM */ } int istgt_bin2hex(char *buf, size_t len, const uint8_t *data, size_t data_len) { const char *digits = "0123456789ABCDEF"; int total = 0; int i; if (len < 3) return -1; buf[total] = '0'; total++; buf[total] = 'x'; total++; buf[total] = '\0'; for (i = 0; i < data_len; i++) { if (total + 3 > len) { buf[total] = '\0'; return - 1; } buf[total] = digits[(data[i] >> 4) & 0x0fU]; total++; buf[total] = digits[data[i] & 0x0fU]; total++; } buf[total] = '\0'; return total; } int istgt_hex2bin(uint8_t *data, size_t data_len, const char *str) { const char *digits = "0123456789ABCDEF"; const char *dp; const char *p; int total = 0; int n0, n1; p = str; if (p[0] != '0' && (p[1] != 'x' && p[1] != 'X')) return -1; p += 2; while (p[0] != '\0' && p[1] != '\0') { if (total >= data_len) { return -1; } dp = strchr(digits, toupper((int) p[0])); if (dp == NULL) { return -1; } n0 = (int) (dp - digits); dp = strchr(digits, toupper((int) p[1])); if (dp == NULL) { return -1; } n1 = (int) (dp - digits); data[total] = (uint8_t) (((n0 & 0x0fU) << 4) | (n1 & 0x0fU)); total++; p += 2; } return total; } void istgt_yield(void) { #if defined (HAVE_PTHREAD_YIELD) pthread_yield(); #elif defined (HAVE_SCHED_YIELD) sched_yield(); #else usleep(0); #endif } #ifndef HAVE_STRLCPY size_t strlcpy(char *dst, const char *src, size_t size) { size_t len; if (dst == NULL) return 0; if (size < 1) { return 0; } len = strlen(src); if (len > size - 1) { len = size - 1; } memcpy(dst, src, len); dst[len] = '\0'; return len; } #endif /* HAVE_STRLCPY */ istgt-20111008/src/istgtcontrol.c000644 000000 000000 00000076277 11357106160 016712 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "build.h" #include #include #include #include #include #include #include #include #include #include "istgt.h" #include "istgt_ver.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_md5.h" //#define TRACE_UCTL #define DEFAULT_UCTL_CONFIG BUILD_ETC_ISTGT "/istgtcontrol.conf" #define DEFAULT_UCTL_TIMEOUT 60 #define DEFAULT_UCTL_PORT 3261 #define DEFAULT_UCTL_HOST "localhost" #define DEFAULT_UCTL_LUN 0 #define DEFAULT_UCTL_MTYPE "-" #define DEFAULT_UCTL_MFLAGS "ro" #define DEFAULT_UCTL_MSIZE "auto" #define MAX_LINEBUF 4096 #define UCTL_CHAP_CHALLENGE_LEN 1024 typedef struct istgt_uctl_auth_t { char *user; char *secret; char *muser; char *msecret; uint8_t chap_id[1]; uint8_t chap_mid[1]; int chap_challenge_len; uint8_t chap_challenge[UCTL_CHAP_CHALLENGE_LEN]; int chap_mchallenge_len; uint8_t chap_mchallenge[UCTL_CHAP_CHALLENGE_LEN]; } UCTL_AUTH; typedef struct istgt_uctl_t { CONFIG *config; char *host; int port; int sock; char *iqn; int lun; char *mflags; char *mfile; char *msize; char *mtype; int family; char caddr[MAX_ADDRBUF]; char saddr[MAX_ADDRBUF]; UCTL_AUTH auth; int timeout; int req_auth_auto; int req_auth; int req_auth_mutual; int recvtmpsize; int recvtmpcnt; int recvtmpidx; int recvbufsize; int sendbufsize; int worksize; char recvtmp[MAX_LINEBUF]; char recvbuf[MAX_LINEBUF]; char sendbuf[MAX_LINEBUF]; char work[MAX_LINEBUF]; char *cmd; char *arg; } UCTL; typedef UCTL *UCTL_Ptr; static void fatal(const char *format, ...) { va_list ap; va_start(ap, format); vfprintf(stderr, format, ap); va_end(ap); exit(EXIT_FAILURE); } typedef enum { UCTL_CMD_OK = 0, UCTL_CMD_ERR = 1, UCTL_CMD_EOF = 2, UCTL_CMD_QUIT = 3, UCTL_CMD_DISCON = 4, UCTL_CMD_REQAUTH = 5, UCTL_CMD_CHAPSEQ = 6, } UCTL_CMD_STATUS; //#define ARGS_DELIM " \t\r\n" #define ARGS_DELIM " \t" static int uctl_readline(UCTL_Ptr uctl) { ssize_t total; total = istgt_readline_socket(uctl->sock, uctl->recvbuf, uctl->recvbufsize, uctl->recvtmp, uctl->recvtmpsize, &uctl->recvtmpidx, &uctl->recvtmpcnt, uctl->timeout); if (total < 0) { return UCTL_CMD_DISCON; } if (total == 0) { return UCTL_CMD_EOF; } return UCTL_CMD_OK; } static int uctl_writeline(UCTL_Ptr uctl) { ssize_t total; ssize_t expect; expect = strlen(uctl->sendbuf); total = istgt_writeline_socket(uctl->sock, uctl->sendbuf, uctl->timeout); if (total < 0) { return UCTL_CMD_DISCON; } if (total != expect) { return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int uctl_snprintf(UCTL_Ptr uctl, const char *format, ...) { va_list ap; int rc; va_start(ap, format); rc = vsnprintf(uctl->sendbuf, uctl->sendbufsize, format, ap); va_end(ap); return rc; } static char * get_banner(UCTL_Ptr uctl) { char *banner; int rc; rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return NULL; } banner = xstrdup(trim_string(uctl->recvbuf)); return banner; } static int is_err_req_auth(UCTL_Ptr uctl, char *s) { const char *req_auth_string = "auth required"; #ifdef TRACE_UCTL printf("S=%s, Q=%s\n", s, req_auth_string); #endif /* TRCAE_UCTL */ if (strncasecmp(s, req_auth_string, strlen(req_auth_string)) == 0) return 1; return 0; } static int is_err_chap_seq(UCTL_Ptr uctl, char *s) { const char *chap_seq_string = "CHAP sequence error"; #ifdef TRACE_UCTL printf("S=%s, Q=%s\n", s, chap_seq_string); #endif /* TRCAE_UCTL */ if (strncasecmp(s, chap_seq_string, strlen(chap_seq_string)) == 0) return 1; return 0; } static int exec_quit(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; int rc; /* send command */ uctl_snprintf(uctl, "QUIT\n"); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_noop(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; int rc; /* send command */ uctl_snprintf(uctl, "NOOP\n"); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_version(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; char *version; char *extver; int rc; /* send command */ uctl_snprintf(uctl, "VERSION\n"); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ while (1) { rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, uctl->cmd) != 0) { break; } version = strsepq(&arg, delim); extver = strsepq(&arg, delim); printf("target version %s %s\n", version, extver); } if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_unload(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; int rc; /* send command */ if (uctl->iqn == NULL || uctl->lun < 0) { return UCTL_CMD_ERR; } uctl_snprintf(uctl, "UNLOAD \"%s\" %d\n", uctl->iqn, uctl->lun); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_load(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; int rc; /* send command */ if (uctl->iqn == NULL || uctl->lun < 0) { return UCTL_CMD_ERR; } uctl_snprintf(uctl, "LOAD \"%s\" %d\n", uctl->iqn, uctl->lun); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_list(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; char *target; int rc; /* send command */ if (uctl->iqn != NULL) { uctl_snprintf(uctl, "LIST \"%s\"\n", uctl->iqn); } else { uctl_snprintf(uctl, "LIST\n"); } rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ while (1) { rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, uctl->cmd) != 0) break; if (uctl->iqn != NULL) { printf("%s\n", arg); } else { target = strsepq(&arg, delim); printf("%s\n", target); } } if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_change(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; int rc; /* send command */ if (uctl->iqn == NULL || uctl->mfile == NULL || uctl->mtype == NULL || uctl->mflags == NULL || uctl->msize == NULL) { return UCTL_CMD_ERR; } uctl_snprintf(uctl, "CHANGE \"%s\" %d \"%s\" " "\"%s\" \"%s\" \"%s\"\n", uctl->iqn, uctl->lun, uctl->mtype, uctl->mflags, uctl->mfile, uctl->msize); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_reset(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; int rc; /* send command */ if (uctl->iqn == NULL || uctl->lun < 0) { return UCTL_CMD_ERR; } uctl_snprintf(uctl, "RESET \"%s\" %d\n", uctl->iqn, uctl->lun); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static int exec_info(UCTL_Ptr uctl) { const char *delim = ARGS_DELIM; char *arg; char *result; int rc; /* send command */ if (uctl->iqn != NULL) { uctl_snprintf(uctl, "INFO \"%s\"\n", uctl->iqn); } else { uctl_snprintf(uctl, "INFO\n"); } rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive result */ while (1) { rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, uctl->cmd) != 0) break; if (uctl->iqn != NULL) { printf("%s\n", arg); } else { printf("%s\n", arg); } } if (strcmp(result, "OK") != 0) { if (is_err_req_auth(uctl, arg)) return UCTL_CMD_REQAUTH; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } typedef struct exec_table_t { const char *name; int (*func) (UCTL_Ptr uctl); int req_argc; int req_target; } EXEC_TABLE; static EXEC_TABLE exec_table[] = { { "QUIT", exec_quit, 0, 0 }, { "NOOP", exec_noop, 0, 0 }, { "VERSION", exec_version, 0, 0 }, { "LIST", exec_list, 0, 0 }, { "UNLOAD", exec_unload, 0, 1 }, { "LOAD", exec_load, 0, 1 }, { "CHANGE", exec_change, 1, 1 }, { "RESET", exec_reset, 0, 1 }, { "INFO", exec_info, 0, 0 }, { NULL, NULL, 0, 0 }, }; static int do_auth(UCTL_Ptr uctl) { uint8_t uctlmd5[ISTGT_MD5DIGEST_LEN]; uint8_t resmd5[ISTGT_MD5DIGEST_LEN]; ISTGT_MD5CTX md5ctx; const char *delim = ARGS_DELIM; char *arg; char *result; char *label; char *chap_i; char *chap_c; char *chap_n; char *chap_r; char *hexmd5; char *hexchallenge; char *workp; int worksize; int algorithm = 5; /* CHAP with MD5 */ int rc; #ifdef TRACE_UCTL printf("do_auth: user=%s, secret=%s, muser=%s, msecret=%s\n", uctl->auth.user, uctl->auth.secret, uctl->auth.muser, uctl->auth.msecret); #endif /* TRACE_UCTL */ /* send algorithm CHAP_A */ uctl_snprintf(uctl, "AUTH CHAP_A %d\n", algorithm); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive CHAP_IC */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "AUTH") != 0) { fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } label = strsepq(&arg, delim); chap_i = strsepq(&arg, delim); chap_c = strsepq(&arg, delim); if (label == NULL || chap_i == NULL || chap_c == NULL) { fprintf(stderr, "CHAP sequence error\n"); return UCTL_CMD_ERR; } if (strcasecmp(label, "CHAP_IC") != 0) { fprintf(stderr, "CHAP sequence error\n"); return UCTL_CMD_ERR; } /* Identifier */ uctl->auth.chap_id[0] = (uint8_t) strtol(chap_i, NULL, 10); /* Challenge Value */ rc = istgt_hex2bin(uctl->auth.chap_challenge, UCTL_CHAP_CHALLENGE_LEN, chap_c); if (rc < 0) { fprintf(stderr, "challenge format error\n"); return UCTL_CMD_ERR; } uctl->auth.chap_challenge_len = rc; if (uctl->auth.user == NULL || uctl->auth.secret == NULL) { fprintf(stderr, "ERROR auth user or secret is missing\n"); return UCTL_CMD_ERR; } istgt_md5init(&md5ctx); /* Identifier */ istgt_md5update(&md5ctx, uctl->auth.chap_id, 1); /* followed by secret */ istgt_md5update(&md5ctx, uctl->auth.secret, strlen(uctl->auth.secret)); /* followed by Challenge Value */ istgt_md5update(&md5ctx, uctl->auth.chap_challenge, uctl->auth.chap_challenge_len); /* uctlmd5 is Response Value */ istgt_md5final(uctlmd5, &md5ctx); workp = uctl->work; worksize = uctl->worksize; istgt_bin2hex(workp, worksize, uctlmd5, ISTGT_MD5DIGEST_LEN); hexmd5 = workp; worksize -= strlen(hexmd5) + 1; workp += strlen(hexmd5) + 1; /* mutual CHAP? */ if (uctl->req_auth_mutual) { /* Identifier is one octet */ istgt_gen_random(uctl->auth.chap_mid, 1); /* Challenge Value is a variable stream of octets */ /* (binary length MUST not exceed 1024 bytes) */ uctl->auth.chap_mchallenge_len = UCTL_CHAP_CHALLENGE_LEN; istgt_gen_random(uctl->auth.chap_mchallenge, uctl->auth.chap_mchallenge_len); istgt_bin2hex(workp, worksize, uctl->auth.chap_mchallenge, uctl->auth.chap_mchallenge_len); hexchallenge = workp; worksize -= strlen(hexchallenge) + 1; workp += strlen(hexchallenge) + 1; /* send CHAP_NR with CHAP_IC */ uctl_snprintf(uctl, "AUTH CHAP_NR %s %s %d %s\n", uctl->auth.user, hexmd5, (int) uctl->auth.chap_mid[0], hexchallenge); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } /* receive CHAP_NR */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "AUTH") != 0) { fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } label = strsepq(&arg, delim); chap_n = strsepq(&arg, delim); chap_r = strsepq(&arg, delim); if (label == NULL || chap_n == NULL || chap_r == NULL) { fprintf(stderr, "CHAP sequence error\n"); return UCTL_CMD_ERR; } if (strcasecmp(label, "CHAP_NR") != 0) { fprintf(stderr, "CHAP sequence error\n"); return UCTL_CMD_ERR; } rc = istgt_hex2bin(resmd5, ISTGT_MD5DIGEST_LEN, chap_r); if (rc < 0 || rc != ISTGT_MD5DIGEST_LEN) { fprintf(stderr, "response format error\n"); return UCTL_CMD_ERR; } if (uctl->auth.muser == NULL || uctl->auth.msecret == NULL) { fprintf(stderr, "ERROR auth user or secret is missing\n"); return UCTL_CMD_ERR; } istgt_md5init(&md5ctx); /* Identifier */ istgt_md5update(&md5ctx, uctl->auth.chap_mid, 1); /* followed by secret */ istgt_md5update(&md5ctx, uctl->auth.msecret, strlen(uctl->auth.msecret)); /* followed by Challenge Value */ istgt_md5update(&md5ctx, uctl->auth.chap_mchallenge, uctl->auth.chap_mchallenge_len); /* uctlmd5 is expecting Response Value */ istgt_md5final(uctlmd5, &md5ctx); /* compare MD5 digest */ if (memcmp(uctlmd5, resmd5, ISTGT_MD5DIGEST_LEN) != 0) { /* not match */ fprintf(stderr, "ERROR auth user or secret is missing\n"); /* discard result line */ if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } /* final with ERR */ return UCTL_CMD_ERR; } } else { /* not mutual */ /* send CHAP_NR */ uctl_snprintf(uctl, "AUTH CHAP_NR %s %s\n", uctl->auth.user, hexmd5); rc = uctl_writeline(uctl); if (rc != UCTL_CMD_OK) { return rc; } } /* receive result */ rc = uctl_readline(uctl); if (rc != UCTL_CMD_OK) { return rc; } arg = trim_string(uctl->recvbuf); result = strsepq(&arg, delim); strupr(result); if (strcmp(result, "OK") != 0) { if (is_err_chap_seq(uctl, arg)) return UCTL_CMD_CHAPSEQ; fprintf(stderr, "ERROR %s\n", arg); return UCTL_CMD_ERR; } return UCTL_CMD_OK; } static char * uctl_get_nmval(CF_SECTION *sp, const char *key, int idx1, int idx2) { CF_ITEM *ip; CF_VALUE *vp; int i; ip = istgt_find_cf_nitem(sp, key, idx1); if (ip == NULL) return NULL; vp = ip->val; if (vp == NULL) return NULL; for (i = 0; vp != NULL; vp = vp->next) { if (i == idx2) return vp->value; i++; } return NULL; } static char * uctl_get_nval(CF_SECTION *sp, const char *key, int idx) { CF_ITEM *ip; CF_VALUE *vp; ip = istgt_find_cf_nitem(sp, key, idx); if (ip == NULL) return NULL; vp = ip->val; if (vp == NULL) return NULL; return vp->value; } static char * uctl_get_val(CF_SECTION *sp, const char *key) { return uctl_get_nval(sp, key, 0); } static int uctl_get_nintval(CF_SECTION *sp, const char *key, int idx) { const char *v; int value; v = uctl_get_nval(sp, key, idx); if (v == NULL) return -1; value = (int)strtol(v, NULL, 10); return value; } static int uctl_get_intval(CF_SECTION *sp, const char *key) { return uctl_get_nintval(sp, key, 0); } static int uctl_init(UCTL_Ptr uctl) { CF_SECTION *sp; const char *val; const char *user, *muser; const char *secret, *msecret; int timeout; int port; int lun; int i; sp = istgt_find_cf_section(uctl->config, "Global"); if (sp == NULL) { fprintf(stderr, "find_cf_section failed()\n"); return -1; } val = uctl_get_val(sp, "Comment"); if (val != NULL) { /* nothing */ #ifdef TRACE_UCTL printf("Comment %s\n", val); #endif /* TRACE_UCTL */ } val = uctl_get_val(sp, "Host"); if (val == NULL) { val = DEFAULT_UCTL_HOST; } uctl->host = xstrdup(val); #ifdef TRACE_UCTL printf("Host %s\n", uctl->host); #endif /* TRACE_UCTL */ port = uctl_get_intval(sp, "Port"); if (port < 0) { port = DEFAULT_UCTL_PORT; } uctl->port = port; #ifdef TRACE_UCTL printf("Port %d\n", uctl->port); #endif /* TRACE_UCTL */ val = uctl_get_val(sp, "TargetName"); if (val == NULL) { val = NULL; } uctl->iqn = xstrdup(val); #ifdef TRACE_UCTL printf("TargetName %s\n", uctl->iqn); #endif /* TRACE_UCTL */ lun = uctl_get_intval(sp, "Lun"); if (lun < 0) { lun = DEFAULT_UCTL_LUN; } uctl->lun = lun; #ifdef TRACE_UCTL printf("Lun %d\n", uctl->lun); #endif /* TRACE_UCTL */ val = uctl_get_val(sp, "Flags"); if (val == NULL) { val = DEFAULT_UCTL_MFLAGS; } uctl->mflags = xstrdup(val); #ifdef TRACE_UCTL printf("Flags %s\n", uctl->mflags); #endif /* TRACE_UCTL */ val = uctl_get_val(sp, "Size"); if (val == NULL) { val = DEFAULT_UCTL_MSIZE; } uctl->msize = xstrdup(val); #ifdef TRACE_UCTL printf("Size %s\n", uctl->msize); #endif /* TRACE_UCTL */ timeout = uctl_get_intval(sp, "Timeout"); if (timeout < 0) { timeout = DEFAULT_UCTL_TIMEOUT; } uctl->timeout = timeout; #ifdef TRACE_UCTL printf("Timeout %d\n", uctl->timeout); #endif /* TRACE_UCTL */ val = uctl_get_val(sp, "AuthMethod"); if (val == NULL) { uctl->req_auth_auto = 0; uctl->req_auth = 0; } else { uctl->req_auth_auto = 0; for (i = 0; ; i++) { val = uctl_get_nmval(sp, "AuthMethod", 0, i); if (val == NULL) break; if (strcasecmp(val, "CHAP") == 0) { uctl->req_auth = 1; } else if (strcasecmp(val, "Mutual") == 0) { uctl->req_auth_mutual = 1; } else if (strcasecmp(val, "Auto") == 0) { uctl->req_auth_auto = 1; uctl->req_auth = 0; uctl->req_auth_mutual = 0; } else { fprintf(stderr, "unknown auth\n"); return -1; } } if (uctl->req_auth_mutual && !uctl->req_auth) { fprintf(stderr, "Mutual but not CHAP\n"); return -1; } } #ifdef TRACE_UCTL if (uctl->req_auth == 0) { printf("AuthMethod Auto\n"); } else { printf("AuthMethod %s %s\n", uctl->req_auth ? "CHAP" : "", uctl->req_auth_mutual ? "Mutual" : ""); } #endif /* TRACE_UCTL */ val = uctl_get_nval(sp, "Auth", 0); if (val == NULL) { user = secret = muser = msecret = NULL; } else { user = uctl_get_nmval(sp, "Auth", 0, 0); secret = uctl_get_nmval(sp, "Auth", 0, 1); muser = uctl_get_nmval(sp, "Auth", 0, 2); msecret = uctl_get_nmval(sp, "Auth", 0, 3); } uctl->auth.user = xstrdup(user); uctl->auth.secret = xstrdup(secret); uctl->auth.muser = xstrdup(muser); uctl->auth.msecret = xstrdup(msecret); #ifdef TRACE_UCTL printf("user=%s, secret=%s, muser=%s, msecret=%s\n", user, secret, muser, msecret); #endif /* TRACE_UCTL */ return 0; } static void usage(void) { printf("istgtcotrol [options] []\n"); printf("options:\n"); printf("default may be changed by configuration file\n"); printf(" -c config config file (default %s)\n", DEFAULT_UCTL_CONFIG); printf(" -h host target host name or IP (default %s)\n", DEFAULT_UCTL_HOST); printf(" -p port port number (default %d)\n", DEFAULT_UCTL_PORT); printf(" -t target target iqn\n"); printf(" -l lun target lun (default %d)\n", DEFAULT_UCTL_LUN); printf(" -f flags media flags (default %s)\n", DEFAULT_UCTL_MFLAGS); printf(" -s size media size (default %s)\n", DEFAULT_UCTL_MSIZE); printf(" -q quiet mode\n"); printf(" -v verbose mode\n"); printf(" -A method authentication method (CHAP/Mutual CHAP/Auto)\n"); printf(" -U user auth user\n"); printf(" -S secret auth secret\n"); printf(" -M muser mutual auth user\n"); printf(" -R msecret mutual auth secret\n"); printf(" -H show this usage\n"); printf(" -V show version\n"); printf("command:\n"); printf(" noop no operation\n"); printf(" version show target version\n"); printf(" list list all or specified target\n"); printf(" load load media to specified unit\n"); printf(" unload unload media from specified unit\n"); printf(" change change media with at specified unit\n"); printf(" reset reset specified lun of target\n"); printf(" info show connections of target\n"); } int main(int argc, char *argv[]) { const char *config_file = DEFAULT_UCTL_CONFIG; CONFIG *config; UCTL xuctl, *uctl; struct sigaction sigact, sigoldact; int (*func) (UCTL_Ptr); int port = -1; int lun = -1; const char *host = NULL; const char *mflags = NULL; const char *mfile = NULL; const char *msize = NULL; const char *mtype = DEFAULT_UCTL_MTYPE; char *target = NULL; char *user = NULL; char *secret = NULL; char *muser = NULL; char *msecret = NULL; char *cmd; char *banner; long l; int exec_result; int req_argc; int req_target; int quiet = 0; int verbose = 0; int req_auth = -1; int ch; int sock; int rc; int i; #ifdef HAVE_SETPROCTITLE setproctitle("version %s (%s)", ISTGT_VERSION, ISTGT_EXTRA_VERSION); #endif memset(&xuctl, 0, sizeof xuctl); uctl = &xuctl; while ((ch = getopt(argc, argv, "c:h:p:t:l:f:s:qvA:U:S:M:R:VH")) != -1) { switch (ch) { case 'c': config_file = optarg; break; case 'h': host = optarg; break; case 'p': l = strtol(optarg, NULL, 10); if (l < 0 || l > 65535) { fatal("invalid port %s\n", optarg); } port = (int) l; break; case 't': target = optarg; break; case 'l': l = strtol(optarg, NULL, 10); if (l < 0 || l > 0x3fffU) { fatal("invalid lun %s\n", optarg); } lun = (int) l; break; case 'f': mflags = optarg; break; case 's': msize = optarg; break; case 'q': quiet = 1; break; case 'v': verbose = 1; break; case 'A': if (strcasecmp(optarg, "CHAP") == 0) { req_auth = 1; } else if (strcasecmp(optarg, "Mutual") == 0 || strcasecmp(optarg, "Mutual CHAP") == 0 || strcasecmp(optarg, "CHAP Mutual") == 0) { req_auth = 2; } else if (strcasecmp(optarg, "Auto") == 0) { req_auth = 0; } else { usage(); exit(EXIT_SUCCESS); } break; case 'U': user = optarg; break; case 'S': secret = optarg; #ifndef HAVE_SETPROCTITLE secret = xstrdup(optarg); memset(optarg, 'x', strlen(optarg)); #endif break; case 'M': muser = optarg; break; case 'R': msecret = optarg; #ifndef HAVE_SETPROCTITLE msecret = xstrdup(optarg); memset(optarg, 'x', strlen(optarg)); #endif break; case 'V': printf("istgtcontrol version %s (%s)\n", ISTGT_VERSION, ISTGT_EXTRA_VERSION); exit(EXIT_SUCCESS); case 'H': default: usage(); exit(EXIT_SUCCESS); } } argc -= optind; argv += optind; /* read config files */ config = istgt_allocate_config(); rc = istgt_read_config(config, config_file); if (rc < 0) { fprintf(stderr, "config error\n"); exit(EXIT_FAILURE); } if (config->section == NULL) { fprintf(stderr, "empty config\n"); istgt_free_config(config); exit(EXIT_FAILURE); } uctl->config = config; //istgt_print_config(config); /* take specified command */ if (argc < 1) { error_usage_return: istgt_free_config(config); usage(); exit(EXIT_FAILURE); } cmd = strupr(xstrdup(argv[0])); argc--; argv++; /* get function pointer and parameters for specified command */ func = NULL; req_argc = -1; req_target = -1; for (i = 0; exec_table[i].name != NULL; i++) { if (cmd[0] == exec_table[i].name[0] && strcmp(cmd, exec_table[i].name) == 0) { func = exec_table[i].func; req_argc = exec_table[i].req_argc; req_target = exec_table[i].req_target; break; } } if (func == NULL) { istgt_free_config(config); fatal("unknown command %s\n", cmd); } /* patrameter check */ if (argc < req_argc) { goto error_usage_return; } #if 0 if (req_target) { if (target == NULL) { goto error_usage_return; } } #endif /* take args */ if (strcmp(cmd, "CHANGE") == 0) { /* change require file */ mfile = argv[0]; } /* build parameters */ uctl_init(uctl); uctl->recvtmpcnt = 0; uctl->recvtmpidx = 0; uctl->recvtmpsize = sizeof uctl->recvtmp; uctl->recvbufsize = sizeof uctl->recvbuf; uctl->sendbufsize = sizeof uctl->sendbuf; uctl->worksize = sizeof uctl->work; /* override by command line */ if (user != NULL) { xfree(uctl->auth.user); uctl->auth.user = xstrdup(user); } if (secret != NULL) { xfree(uctl->auth.secret); uctl->auth.secret = xstrdup(secret); } if (muser != NULL) { xfree(uctl->auth.muser); uctl->auth.muser = xstrdup(muser); } if (msecret != NULL) { xfree(uctl->auth.msecret); uctl->auth.msecret = xstrdup(msecret); } if (req_target) { if (uctl->iqn == NULL && target == NULL) { goto error_usage_return; } } if (req_auth >= 0) { uctl->req_auth_auto = 1; uctl->req_auth = 0; uctl->req_auth_mutual = 0; if (req_auth > 1) { uctl->req_auth_auto = 0; uctl->req_auth = 1; uctl->req_auth_mutual = 1; } else if (req_auth > 0) { uctl->req_auth_auto = 0; uctl->req_auth = 1; } } #ifdef TRACE_UCTL printf("auto=%d, auth=%d, mutual=%d\n", uctl->req_auth_auto, uctl->req_auth, uctl->req_auth_mutual); #endif /* TRACE_UCTL */ if (host != NULL) { xfree(uctl->host); uctl->host = xstrdup(host); } if (port >= 0) { uctl->port = port; } if (target != NULL) { xfree(uctl->iqn); if (strcasecmp(target, "ALL") == 0) { uctl->iqn = NULL; } else { uctl->iqn = escape_string(target); } } if (lun >= 0) { uctl->lun = lun; } if (mflags != NULL) { xfree(uctl->mflags); uctl->mflags = escape_string(mflags); } uctl->mfile = escape_string(mfile); if (msize != NULL) { xfree(uctl->msize); uctl->msize = escape_string(msize); } uctl->mtype = escape_string(mtype); uctl->cmd = escape_string(cmd); /* show setting */ #define NULLP(S) ((S) == NULL ? "NULL" : (S)) if (verbose) { printf("iqn=%s, lun=%d\n", NULLP(uctl->iqn), uctl->lun); printf("media file=%s, flags=%s, size=%s\n", NULLP(uctl->mfile), NULLP(uctl->mflags), NULLP(uctl->msize)); } /* set signals */ memset(&sigact, 0, sizeof sigact); memset(&sigoldact, 0, sizeof sigoldact); sigact.sa_handler = SIG_IGN; sigemptyset(&sigact.sa_mask); if (sigaction(SIGPIPE, &sigact, &sigoldact) != 0) { istgt_free_config(config); fatal("sigaction() failed"); } /* connect to target */ if (verbose) { printf("connect to %s:%d\n", uctl->host, uctl->port); } sock = istgt_connect(uctl->host, uctl->port); if (sock < 0) { istgt_free_config(config); fatal("istgt_connect(%s:%d) failed\n", uctl->host, uctl->port); } uctl->sock = sock; /* get target banner (ready to send) */ banner = get_banner(uctl); if (banner == NULL) { close(uctl->sock); istgt_free_config(config); fatal("get_banner() failed\n"); } if (verbose) { printf("target banner \"%s\"\n", banner); } /* authentication */ retry_auth: if (uctl->req_auth) { rc = do_auth(uctl); if (rc != UCTL_CMD_OK) { if (rc == UCTL_CMD_REQAUTH || rc == UCTL_CMD_CHAPSEQ) { retry_auth_auto: /* Auth negotiation */ if (uctl->req_auth == 0) { #ifdef TRCAE_UCTL printf("Auto negotiation CHAP\n"); #endif /* TRCAE_UCTL */ uctl->req_auth = 1; goto retry_auth; } else if (uctl->req_auth_mutual == 0) { #ifdef TRCAE_UCTL printf("Auto negotiation Mutual CHAP\n"); #endif /* TRCAE_UCTL */ uctl->req_auth_mutual = 1; goto retry_auth; } } if (!quiet) { printf("AUTH failed\n"); } exec_result = rc; goto disconnect; } } /* send specified command */ rc = func(uctl); exec_result = rc; if (rc != UCTL_CMD_OK) { if (rc == UCTL_CMD_REQAUTH || rc == UCTL_CMD_CHAPSEQ) { goto retry_auth_auto; } if (!quiet) { printf("ABORT %s command\n", uctl->cmd); } } else { if (!quiet) { printf("DONE %s command\n", uctl->cmd); } } /* disconnect from target */ disconnect: rc = exec_quit(uctl); if (rc != UCTL_CMD_OK) { fprintf(stderr, "QUIT failed\n"); /* error but continue */ } /* cleanup */ close(sock); xfree(uctl->host); xfree(uctl->iqn); xfree(uctl->mflags); xfree(uctl->mfile); xfree(uctl->msize); xfree(uctl->mtype); xfree(uctl->cmd); xfree(banner); xfree(cmd); istgt_free_config(config); /* return value as execution result */ if (exec_result != UCTL_CMD_OK) { exit(EXIT_FAILURE); } return EXIT_SUCCESS; } istgt-20111008/src/istgt_queue.h000644 000000 000000 00000003622 11317704406 016505 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_QUEUE_H #define ISTGT_QUEUE_H #include typedef struct istgt_queue_t { struct istgt_queue_t *prev; struct istgt_queue_t *next; void *elem; } ISTGT_QUEUE; typedef ISTGT_QUEUE *ISTGT_QUEUE_Ptr; int istgt_queue_init(ISTGT_QUEUE_Ptr head); void istgt_queue_destroy(ISTGT_QUEUE_Ptr head); int istgt_queue_count(ISTGT_QUEUE_Ptr head); int istgt_queue_enqueue(ISTGT_QUEUE_Ptr head, void *elem); void *istgt_queue_dequeue(ISTGT_QUEUE_Ptr head); int istgt_queue_enqueue_first(ISTGT_QUEUE_Ptr head, void *elem); #endif /* ISTGT_QUEUE_H */ istgt-20111008/src/istgt_conf.h000644 000000 000000 00000004551 11317704406 016310 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_CONF_H #define ISTGT_CONF_H typedef struct config_value_t { struct config_value_t *next; char *value; } CF_VALUE; typedef struct config_item_t { struct config_item_t *next; char *key; CF_VALUE *val; } CF_ITEM; typedef enum { ST_INVALID = -1, ST_NONE = 0, ST_GLOBAL, ST_UNITCONTROL, ST_PORTALGROUP, ST_INITIATORGROUP, ST_LOGICAL_UNIT, ST_AUTHGROUP, } CF_SECTION_TYPE; typedef struct config_section_t { struct config_section_t *next; CF_SECTION_TYPE type; char *name; int num; CF_ITEM *item; } CF_SECTION; typedef struct config_t { char *file; CF_SECTION *current_section; CF_SECTION *section; } CONFIG; CONFIG *istgt_allocate_config(void); void istgt_free_config(CONFIG *cp); CF_SECTION *istgt_find_cf_section(CONFIG *cp, const char *name); CF_ITEM *istgt_find_cf_nitem(CF_SECTION *sp, const char *key, int idx); CF_ITEM *istgt_find_cf_item(CF_SECTION *sp, const char *key); int istgt_read_config(CONFIG *cp, const char *file); int istgt_print_config(CONFIG *cp); #endif /* ISTGT_CONF_H */ istgt-20111008/src/istgt_lu_pass.c000644 000000 000000 00000137323 11530752361 017030 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #ifdef HAVE_LIBCAM #include #include #include #include #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_misc.h" #include "istgt_lu.h" #include "istgt_proto.h" #include "istgt_scsi.h" //#define ISTGT_TRACE_PASS #define ISTGT_LU_CAM_TIMEOUT 60000 /* 60sec. */ typedef struct istgt_lu_pass_t { ISTGT_LU_Ptr lu; int num; int lun; const char *file; uint64_t size; uint64_t blocklen; uint64_t blockcnt; char *device; int unit; struct cam_device *cam_dev; union ccb *ccb; int timeout; uint8_t *inq_standard; int inq_standard_len; int inq_pd; int inq_rmb; int inq_ver; int inq_fmt; uint64_t ms_blocklen; uint64_t ms_blockcnt; } ISTGT_LU_PASS; #define BUILD_SENSE(SK,ASC,ASCQ) \ do { \ *sense_len = \ istgt_lu_pass_build_sense_data(spec, sense_data, \ ISTGT_SCSI_SENSE_ ## SK, \ (ASC), (ASCQ)); \ } while (0) static int istgt_lu_pass_build_sense_data(ISTGT_LU_PASS *spec, uint8_t *data, int sk, int asc, int ascq); static void istgt_lu_pass_parse_sense_key(uint8_t *sense_data, int *skp, int *ascp, int *ascqp) { int rsp; int sk, asc, ascq; if (sense_data == NULL) { if (skp != NULL) *skp = -1; if (ascp != NULL) *ascp = -1; if (ascqp != NULL) *ascqp = -1; return; } rsp = BGET8W(&sense_data[0], 6, 7); switch (rsp) { case 0x70: /* Current Fixed */ sk = BGET8W(&sense_data[2], 3, 4); asc = sense_data[12]; ascq = sense_data[13]; break; case 0x71: /* Deferred Fixed */ sk = BGET8W(&sense_data[2], 3, 4); asc = sense_data[12]; ascq = sense_data[13]; break; case 0x72: /* Current Descriptor */ sk = BGET8W(&sense_data[2], 3, 4); asc = sense_data[2]; ascq = sense_data[3]; break; case 0x73: /* Deferred Descriptor */ sk = BGET8W(&sense_data[2], 3, 4); asc = sense_data[2]; ascq = sense_data[3]; break; default: sk = asc = ascq = -1; break; } if (skp != NULL) *skp = sk; if (ascp != NULL) *ascp = asc; if (ascqp != NULL) *ascqp = ascq; } static void istgt_lu_pass_print_sense_key(uint8_t *sense_data) { int sk, asc, ascq; istgt_lu_pass_parse_sense_key(sense_data, &sk, &asc, &ascq); if (sk >= 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "SK=0x%x, ASC=0x%x, ASCQ=0x%x\n", sk, asc, ascq); } } static int istgt_lu_pass_set_inquiry(ISTGT_LU_PASS *spec) { uint8_t buf[MAX_TMPBUF]; uint8_t cdb[16]; uint32_t flags; uint8_t *data; int cdb_len; int data_len; int data_alloc_len; int retry = 1; int rc; memset(buf, 0, sizeof buf); memset(cdb, 0, sizeof cdb); data = buf; if (sizeof buf > 0xff) { data_alloc_len = 0xff; } else { data_alloc_len = sizeof buf; } /* issue standard INQUIRY */ cdb[0] = SPC_INQUIRY; cdb[1] = 0; cdb[2] = 0; DSET16(&cdb[3], data_alloc_len); /* ALLOCATION LENGTH */ cdb[5] = 0; cdb_len = 6; ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, cdb_len); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); flags = CAM_DIR_IN; flags |= CAM_DEV_QFRZDIS; cam_fill_csio(&spec->ccb->csio, retry, NULL, flags, MSG_SIMPLE_Q_TAG, data, data_alloc_len, SSD_FULL_SIZE, cdb_len, spec->timeout); rc = cam_send_ccb(spec->cam_dev, spec->ccb); if (rc < 0) { ISTGT_ERRLOG("cam_send_ccb() failed\n"); return -1; } if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request error CAM=0x%x, SCSI=0x%x\n", spec->ccb->ccb_h.status, spec->ccb->csio.scsi_status); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SENSE", (uint8_t *) &spec->ccb->csio.sense_data, SSD_FULL_SIZE); istgt_lu_pass_print_sense_key((uint8_t *) &spec->ccb->csio.sense_data); return -1; } data_len = spec->ccb->csio.dxfer_len; data_len -= spec->ccb->csio.resid; ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "INQUIRY", data, data_len); spec->inq_standard = xmalloc(data_len); spec->inq_standard_len = data_len; memcpy(spec->inq_standard, data, data_len); return 0; } static int istgt_lu_pass_set_modesense(ISTGT_LU_PASS *spec) { uint8_t buf[MAX_TMPBUF]; uint8_t cdb[16]; uint32_t flags; uint8_t *data; int cdb_len; int data_len; int data_alloc_len; int req_len; int retry = 1; int sk, asc, ascq; int rc; memset(buf, 0, sizeof buf); memset(cdb, 0, sizeof cdb); data = buf; if (sizeof buf > 0xff) { data_alloc_len = 0xff; } else { data_alloc_len = sizeof buf; } if (spec->inq_pd == SPC_PERIPHERAL_DEVICE_TYPE_DVD) { /* MMC have only 10 */ goto retry_sense10; } retry_sense6: spec->ms_blockcnt = 0; spec->ms_blocklen = 0; memset(cdb, 0, sizeof cdb); /* issue MODE SENSE(6) */ data_alloc_len = 4 + 8; /* only block descriptor */ req_len = 4 + 8; cdb[0] = SPC_MODE_SENSE_6; BDADD8(&cdb[1], 0, 3); /* DBD */ BDSET8W(&cdb[2], 0x00, 7, 2); /* PC */ //BDADD8W(&cdb[2], 0x00, 5, 6); /* PAGE CODE */ BDADD8W(&cdb[2], 0x3f, 5, 6); /* PAGE CODE */ cdb[3] = 0x00; /* SUBPAGE CODE */ cdb[4] = data_alloc_len; /* ALLOCATION LENGTH */ cdb_len = 6; ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, cdb_len); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); flags = CAM_DIR_IN; flags |= CAM_DEV_QFRZDIS; cam_fill_csio(&spec->ccb->csio, retry, NULL, flags, MSG_SIMPLE_Q_TAG, data, data_alloc_len, SSD_FULL_SIZE, cdb_len, spec->timeout); rc = cam_send_ccb(spec->cam_dev, spec->ccb); if (rc < 0) { ISTGT_ERRLOG("cam_send_ccb() failed\n"); return -1; } if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request error CAM=0x%x, SCSI=0x%x\n", spec->ccb->ccb_h.status, spec->ccb->csio.scsi_status); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SENSE", (uint8_t *) &spec->ccb->csio.sense_data, SSD_FULL_SIZE); istgt_lu_pass_print_sense_key((uint8_t *) &spec->ccb->csio.sense_data); istgt_lu_pass_parse_sense_key((uint8_t *) &spec->ccb->csio.sense_data, &sk, &asc, &ascq); if (sk == ISTGT_SCSI_SENSE_ILLEGAL_REQUEST) { if (asc == 0x20 && ascq == 0x00) { /* INVALID COMMAND OPERATION CODE */ goto retry_sense10; } else if (asc == 0x24 && ascq == 0x00) { /* INVALID FIELD IN CDB */ goto retry_sense10; } } if (sk == ISTGT_SCSI_SENSE_UNIT_ATTENTION) { if (asc == 0x28 && ascq == 0x00) { /* NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED */ goto retry_sense6; } if (asc == 0x29 && ascq == 0x00) { /* POWER ON, RESET, OR BUS DEVICE RESET OCCURRED */ goto retry_sense6; } else if (asc == 0x29 && ascq == 0x01) { /* POWER ON OCCURRED */ goto retry_sense6; } else if (asc == 0x29 && ascq == 0x02) { /* SCSI BUS RESET OCCURRED */ goto retry_sense6; } else if (asc == 0x29 && ascq == 0x03) { /* BUS DEVICE RESET FUNCTION OCCURRED */ goto retry_sense6; } else if (asc == 0x29 && ascq == 0x04) { /* DEVICE INTERNAL RESET */ goto retry_sense6; } else if (asc == 0x29 && ascq == 0x05) { /* TRANSCEIVER MODE CHANGED TO SINGLE-ENDED */ goto retry_sense6; } else if (asc == 0x29 && ascq == 0x06) { /* TRANSCEIVER MODE CHANGED TO LVD */ goto retry_sense6; } else if (asc == 0x29 && ascq == 0x07) { /* I_T NEXUS LOSS OCCURRED */ goto retry_sense6; } } return -1; } data_len = spec->ccb->csio.dxfer_len; data_len -= spec->ccb->csio.resid; if (data_len < req_len) { ISTGT_ERRLOG("result is short\n"); return -1; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "MODE SENSE(6)", data, data_len); if (DGET8(&data[3]) != 0) { /* BLOCK DESCRIPTOR LENGTH */ if (spec->inq_pd == SPC_PERIPHERAL_DEVICE_TYPE_DISK) { spec->ms_blockcnt = DGET32(&data[4+0]); spec->ms_blocklen = DGET24(&data[4+5]); } else { spec->ms_blockcnt = DGET24(&data[4+1]); spec->ms_blocklen = DGET24(&data[4+5]); } } else { goto retry_sense10; } if ((spec->inq_pd == SPC_PERIPHERAL_DEVICE_TYPE_DISK && spec->ms_blockcnt == 0xffffffffU) || (spec->inq_pd != SPC_PERIPHERAL_DEVICE_TYPE_DISK && spec->ms_blockcnt == 0x00ffffffU)) { retry_sense10: spec->ms_blockcnt = 0; spec->ms_blocklen = 0; memset(cdb, 0, sizeof cdb); /* issue MODE SENSE(10) */ data_alloc_len = 8 + 16; /* only block descriptor */ req_len = 8 + 16; cdb[0] = SPC_MODE_SENSE_10; BDSET8(&cdb[1], 1, 4); /* LLBAA */ BDADD8(&cdb[1], 0, 3); /* DBD */ BDSET8W(&cdb[2], 0x00, 7, 2); /* PC */ //BDADD8W(&cdb[2], 0x00, 5, 6); /* PAGE CODE */ BDADD8W(&cdb[2], 0x3f, 5, 6); /* PAGE CODE */ cdb[3] = 0x00; /* SUBPAGE CODE */ DSET16(&cdb[7], data_alloc_len); /* ALLOCATION LENGTH */ cdb_len = 10; ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, cdb_len); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); flags = CAM_DIR_IN; flags |= CAM_DEV_QFRZDIS; cam_fill_csio(&spec->ccb->csio, retry, NULL, flags, MSG_SIMPLE_Q_TAG, data, data_alloc_len, SSD_FULL_SIZE, cdb_len, spec->timeout); rc = cam_send_ccb(spec->cam_dev, spec->ccb); if (rc < 0) { ISTGT_ERRLOG("cam_send_ccb() failed\n"); return -1; } if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request error CAM=0x%x, SCSI=0x%x\n", spec->ccb->ccb_h.status, spec->ccb->csio.scsi_status); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SENSE", (uint8_t *) &spec->ccb->csio.sense_data, SSD_FULL_SIZE); istgt_lu_pass_print_sense_key((uint8_t *) &spec->ccb->csio.sense_data); istgt_lu_pass_parse_sense_key((uint8_t *) &spec->ccb->csio.sense_data, &sk, &asc, &ascq); if (sk == ISTGT_SCSI_SENSE_ILLEGAL_REQUEST) { if (spec->inq_ver < SPC_VERSION_SPC3) { //ISTGT_WARNLOG("MODE SENSE was not supported\n"); return 0; } if (asc == 0x20 && ascq == 0x00) { /* INVALID COMMAND OPERATION CODE */ return 0; } else if (asc == 0x24 && ascq == 0x00) { /* INVALID FIELD IN CDB */ return 0; } } if (sk == ISTGT_SCSI_SENSE_UNIT_ATTENTION) { if (asc == 0x28 && ascq == 0x00) { /* NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED */ goto retry_sense10; } if (asc == 0x29 && ascq == 0x00) { /* POWER ON, RESET, OR BUS DEVICE RESET OCCURRED */ goto retry_sense10; } else if (asc == 0x29 && ascq == 0x01) { /* POWER ON OCCURRED */ goto retry_sense10; } else if (asc == 0x29 && ascq == 0x02) { /* SCSI BUS RESET OCCURRED */ goto retry_sense10; } else if (asc == 0x29 && ascq == 0x03) { /* BUS DEVICE RESET FUNCTION OCCURRED */ goto retry_sense10; } else if (asc == 0x29 && ascq == 0x04) { /* DEVICE INTERNAL RESET */ goto retry_sense10; } else if (asc == 0x29 && ascq == 0x05) { /* TRANSCEIVER MODE CHANGED TO SINGLE-ENDED */ goto retry_sense10; } else if (asc == 0x29 && ascq == 0x06) { /* TRANSCEIVER MODE CHANGED TO LVD */ goto retry_sense10; } else if (asc == 0x29 && ascq == 0x07) { /* I_T NEXUS LOSS OCCURRED */ goto retry_sense10; } } return -1; } data_len = spec->ccb->csio.dxfer_len; data_len -= spec->ccb->csio.resid; if (data_len < req_len) { ISTGT_ERRLOG("result is short\n"); return -1; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "MODE SENSE(10)", data, data_len); if (DGET16(&data[6]) != 0) { /* BLOCK DESCRIPTOR LENGTH */ spec->ms_blockcnt = DGET64(&data[8+0]); spec->ms_blocklen = DGET32(&data[8+12]); } } return 0; } static int istgt_lu_pass_set_capacity(ISTGT_LU_PASS *spec) { uint8_t buf[MAX_TMPBUF]; uint8_t cdb[16]; uint32_t flags; uint8_t *data; int cdb_len; int data_len; int data_alloc_len; int req_len; int retry = 1; int sk, asc, ascq; int rc; memset(buf, 0, sizeof buf); memset(cdb, 0, sizeof cdb); data = buf; if (sizeof buf > 0xff) { data_alloc_len = 0xff; } else { data_alloc_len = sizeof buf; } /* issue READ CAPACITY (10) */ retry_capacity10: memset(cdb, 0, sizeof cdb); data_alloc_len = 8; req_len = 8; if (spec->inq_pd == SPC_PERIPHERAL_DEVICE_TYPE_DISK) { cdb[0] = SBC_READ_CAPACITY_10; cdb_len = 10; } else if (spec->inq_pd == SPC_PERIPHERAL_DEVICE_TYPE_DVD) { cdb[0] = MMC_READ_CAPACITY; cdb_len = 10; } else { ISTGT_ERRLOG("unsupported device\n"); return -1; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, cdb_len); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); flags = CAM_DIR_IN; flags |= CAM_DEV_QFRZDIS; cam_fill_csio(&spec->ccb->csio, retry, NULL, flags, MSG_SIMPLE_Q_TAG, data, data_alloc_len, SSD_FULL_SIZE, cdb_len, spec->timeout); rc = cam_send_ccb(spec->cam_dev, spec->ccb); if (rc < 0) { ISTGT_ERRLOG("cam_send_ccb() failed\n"); return -1; } if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request error CAM=0x%x, SCSI=0x%x\n", spec->ccb->ccb_h.status, spec->ccb->csio.scsi_status); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SENSE", (uint8_t *) &spec->ccb->csio.sense_data, SSD_FULL_SIZE); istgt_lu_pass_print_sense_key((uint8_t *) &spec->ccb->csio.sense_data); istgt_lu_pass_parse_sense_key((uint8_t *) &spec->ccb->csio.sense_data, &sk, &asc, &ascq); if (sk == ISTGT_SCSI_SENSE_NOT_READY) { if (asc == 0x04 && ascq == 0x01) { /* LOGICAL UNIT IS IN PROCESS OF BECOMING READY */ sleep(2); goto retry_capacity10; } if (asc == 0x3a && ascq == 0x00) { /* MEDIUM NOT PRESENT */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x01) { /* MEDIUM NOT PRESENT - TRAY CLOSED */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x02) { /* MEDIUM NOT PRESENT - TRAY OPEN */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x03) { /* MEDIUM NOT PRESENT - LOADABLE */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x04) { /* MEDIUM NOT PRESENT - MEDIUM AUXILIARY MEMORY ACCESSIBLE */ goto medium_not_present; } ISTGT_ERRLOG("device not ready\n"); return -1; } if (sk == ISTGT_SCSI_SENSE_UNIT_ATTENTION) { if (asc == 0x28 && ascq == 0x00) { /* NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED */ goto retry_capacity10; } if (asc == 0x29 && ascq == 0x00) { /* POWER ON, RESET, OR BUS DEVICE RESET OCCURRED */ goto retry_capacity10; } else if (asc == 0x29 && ascq == 0x01) { /* POWER ON OCCURRED */ goto retry_capacity10; } else if (asc == 0x29 && ascq == 0x02) { /* SCSI BUS RESET OCCURRED */ goto retry_capacity10; } else if (asc == 0x29 && ascq == 0x03) { /* BUS DEVICE RESET FUNCTION OCCURRED */ goto retry_capacity10; } else if (asc == 0x29 && ascq == 0x04) { /* DEVICE INTERNAL RESET */ goto retry_capacity10; } else if (asc == 0x29 && ascq == 0x05) { /* TRANSCEIVER MODE CHANGED TO SINGLE-ENDED */ goto retry_capacity10; } else if (asc == 0x29 && ascq == 0x06) { /* TRANSCEIVER MODE CHANGED TO LVD */ goto retry_capacity10; } else if (asc == 0x29 && ascq == 0x07) { /* I_T NEXUS LOSS OCCURRED */ goto retry_capacity10; } } return -1; } data_len = spec->ccb->csio.dxfer_len; data_len -= spec->ccb->csio.resid; if (data_len < req_len) { ISTGT_ERRLOG("result is short\n"); return -1; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "READ CAPACITY(10)", data, data_len); spec->blockcnt = (uint64_t) DGET32(&data[0]); // last LBA spec->blocklen = (uint64_t) DGET32(&data[4]); if (spec->blockcnt == 0xffffffffU) { retry_capacity16: memset(cdb, 0, sizeof cdb); /* issue READ CAPACITY(16) */ data_alloc_len = 32; req_len = 32; if (spec->inq_pd == SPC_PERIPHERAL_DEVICE_TYPE_DISK) { cdb[0] = SPC_SERVICE_ACTION_IN_16; /* SERVICE ACTION */ BDSET8W(&cdb[1], SBC_SAI_READ_CAPACITY_16, 4, 5); /* ALLOCATION LENGTH */ DSET16(&cdb[10], data_alloc_len); cdb_len = 16; } else if (spec->inq_pd == SPC_PERIPHERAL_DEVICE_TYPE_DVD) { ISTGT_ERRLOG("unsupported device\n"); return -1; } else { ISTGT_ERRLOG("unsupported device\n"); return -1; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, cdb_len); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); flags = CAM_DIR_IN; flags |= CAM_DEV_QFRZDIS; cam_fill_csio(&spec->ccb->csio, retry, NULL, flags, MSG_SIMPLE_Q_TAG, data, data_alloc_len, SSD_FULL_SIZE, cdb_len, spec->timeout); rc = cam_send_ccb(spec->cam_dev, spec->ccb); if (rc < 0) { ISTGT_ERRLOG("cam_send_ccb() failed\n"); return -1; } if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request error CAM=0x%x, SCSI=0x%x\n", spec->ccb->ccb_h.status, spec->ccb->csio.scsi_status); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SENSE", (uint8_t *) &spec->ccb->csio.sense_data, SSD_FULL_SIZE); istgt_lu_pass_print_sense_key((uint8_t *) &spec->ccb->csio.sense_data); istgt_lu_pass_parse_sense_key((uint8_t *) &spec->ccb->csio.sense_data, &sk, &asc, &ascq); if (sk == ISTGT_SCSI_SENSE_NOT_READY) { if (asc == 0x04 && ascq == 0x01) { /* LOGICAL UNIT IS IN PROCESS OF BECOMING READY */ sleep(2); goto retry_capacity16; } if (asc == 0x3a && ascq == 0x00) { /* MEDIUM NOT PRESENT */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x01) { /* MEDIUM NOT PRESENT - TRAY CLOSED */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x02) { /* MEDIUM NOT PRESENT - TRAY OPEN */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x03) { /* MEDIUM NOT PRESENT - LOADABLE */ goto medium_not_present; } else if (asc == 0x3a && ascq == 0x04) { /* MEDIUM NOT PRESENT - MEDIUM AUXILIARY MEMORY ACCESSIBLE */ goto medium_not_present; } ISTGT_ERRLOG("device not ready\n"); return -1; } if (sk == ISTGT_SCSI_SENSE_UNIT_ATTENTION) { if (asc == 0x28 && ascq == 0x00) { /* NOT READY TO READY CHANGE, MEDIUM MAY HAVE CHANGED */ goto retry_capacity16; } if (asc == 0x29 && ascq == 0x00) { /* POWER ON, RESET, OR BUS DEVICE RESET OCCURRED */ goto retry_capacity16; } else if (asc == 0x29 && ascq == 0x01) { /* POWER ON OCCURRED */ goto retry_capacity16; } else if (asc == 0x29 && ascq == 0x02) { /* SCSI BUS RESET OCCURRED */ goto retry_capacity16; } else if (asc == 0x29 && ascq == 0x03) { /* BUS DEVICE RESET FUNCTION OCCURRED */ goto retry_capacity16; } else if (asc == 0x29 && ascq == 0x04) { /* DEVICE INTERNAL RESET */ goto retry_capacity16; } else if (asc == 0x29 && ascq == 0x05) { /* TRANSCEIVER MODE CHANGED TO SINGLE-ENDED */ goto retry_capacity16; } else if (asc == 0x29 && ascq == 0x06) { /* TRANSCEIVER MODE CHANGED TO LVD */ goto retry_capacity16; } else if (asc == 0x29 && ascq == 0x07) { /* I_T NEXUS LOSS OCCURRED */ goto retry_capacity16; } } return -1; } data_len = spec->ccb->csio.dxfer_len; data_len -= spec->ccb->csio.resid; if (data_len < req_len) { ISTGT_ERRLOG("result is short\n"); return -1; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "READ CAPACITY(16)", data, data_len); spec->blockcnt = DGET64(&data[0]); // last LBA spec->blocklen = (uint64_t) DGET32(&data[8]); } spec->blockcnt++; spec->size = spec->blockcnt * spec->blocklen; return 0; medium_not_present: spec->blockcnt = 0; spec->blocklen = 0; spec->size = 0; return 0; } int istgt_lu_pass_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { char buf[MAX_TMPBUF]; ISTGT_LU_PASS *spec; uint64_t gb_size; uint64_t mb_size; int mb_digit; int flags; int rc; int pq, pd, rmb; int ver, fmt; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_pass_init\n"); printf("LU%d PASS-THROUGH UNIT\n", lu->num); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); lu->lun[i].spec = NULL; continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_DEVICE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d device\n", lu->num, i); spec = xmalloc(sizeof *spec); memset(spec, 0, sizeof *spec); spec->lu = lu; spec->num = lu->num; spec->lun = i; spec->timeout = ISTGT_LU_CAM_TIMEOUT; spec->inq_standard = NULL; spec->inq_standard_len = 0; spec->inq_pd = 0; spec->inq_rmb = 0; spec->inq_ver = 0; spec->file = lu->lun[i].u.device.file; spec->size = 0; spec->blocklen = 0; spec->blockcnt = 0; printf("LU%d: LUN%d file=%s\n", lu->num, i, spec->file); flags = lu->readonly ? O_RDONLY : O_RDWR; rc = cam_get_device(spec->file, buf, sizeof buf, &spec->unit); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: cam_get_device() failed\n", lu->num, i); xfree(spec); return -1; } spec->device = xstrdup(buf); spec->cam_dev = cam_open_spec_device(spec->device, spec->unit, flags, NULL); if (spec->cam_dev == NULL) { ISTGT_ERRLOG("LU%d: LUN%d: cam_open() failed\n", lu->num, i); xfree(spec->device); xfree(spec); return -1; } spec->ccb = cam_getccb(spec->cam_dev); if (spec->ccb == NULL) { ISTGT_ERRLOG("LU%d: LUN%d: cam_getccb() failed\n", lu->num, i); cam_close_spec_device(spec->cam_dev); xfree(spec->device); xfree(spec); return -1; } memset((uint8_t *) spec->ccb + sizeof(struct ccb_hdr), 0, sizeof(struct ccb_scsiio) - sizeof(struct ccb_hdr)); rc = istgt_lu_pass_set_inquiry(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_pass_set_inquiry() failed\n", lu->num, i); error_return: cam_freeccb(spec->ccb); cam_close_spec_device(spec->cam_dev); xfree(spec->device); xfree(spec); return -1; } /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ pq = BGET8W(&spec->inq_standard[0], 7, 3); pd = BGET8W(&spec->inq_standard[0], 4, 5); /* RMB(7) */ rmb = BGET8W(&spec->inq_standard[1], 7, 1); /* VERSION ANSI(2-0) */ ver = BGET8W(&spec->inq_standard[2], 2, 3); /* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */ fmt = BGET8W(&spec->inq_standard[3], 3, 4); printf("LU%d: LUN%d pq=0x%x, pd=0x%x, rmb=%d, ver=%d, fmt=%d\n", lu->num, i, pq, pd, rmb, ver, fmt); if (pq != 0x00) { ISTGT_ERRLOG("unsupported peripheral qualifier (%x)\n", pq); goto error_return; } switch (pd) { case SPC_PERIPHERAL_DEVICE_TYPE_DISK: printf("LU%d: LUN%d Direct access block device\n", lu->num, i); break; case SPC_PERIPHERAL_DEVICE_TYPE_TAPE: printf("LU%d: LUN%d Sequential-access device\n", lu->num, i); break; case SPC_PERIPHERAL_DEVICE_TYPE_DVD: printf("LU%d: LUN%d CD/DVD device\n", lu->num, i); break; case SPC_PERIPHERAL_DEVICE_TYPE_CHANGER: printf("LU%d: LUN%d Medium changer device\n", lu->num, i); break; default: ISTGT_ERRLOG("unsupported peripheral device type (%x)\n", pd); goto error_return; } switch (ver) { case SPC_VERSION_NONE: printf("LU%d: LUN%d version NONE\n", lu->num, i); break; case SPC_VERSION_SPC: printf("LU%d: LUN%d version SPC\n", lu->num, i); break; case SPC_VERSION_SPC2: printf("LU%d: LUN%d version SPC2\n", lu->num, i); break; case SPC_VERSION_SPC3: printf("LU%d: LUN%d version SPC3\n", lu->num, i); break; case SPC_VERSION_SPC4: printf("LU%d: LUN%d version SPC4\n", lu->num, i); break; case 0x01: printf("LU%d: LUN%d version SCSI1\n", lu->num, i); break; case 0x02: printf("LU%d: LUN%d version SCSI2\n", lu->num, i); break; default: ISTGT_ERRLOG("LU%d: LUN%d: unsupported version(%d)\n", lu->num, i, ver); goto error_return; } switch (fmt) { case 0x00: printf("LU%d: LUN%d format SCSI1\n", lu->num, i); break; case 0x01: printf("LU%d: LUN%d format CCS\n", lu->num, i); break; case 0x02: printf("LU%d: LUN%d format SCSI2/SPC\n", lu->num, i); break; default: ISTGT_ERRLOG("LU%d: LUN%d: unsupported format(%d)\n", lu->num, i, fmt); goto error_return; } spec->inq_pd = pd; spec->inq_rmb = rmb; spec->inq_ver = ver; spec->inq_fmt = fmt; if (pd != SPC_PERIPHERAL_DEVICE_TYPE_CHANGER) { rc = istgt_lu_pass_set_modesense(spec); if (rc < 0) { #if 0 ISTGT_ERRLOG("LU%d: LUN%d: lu_pass_set_modesense() failed\n", lu->num, i); goto error_return; #else spec->ms_blockcnt = 0; spec->ms_blocklen = 0; #endif } } else { spec->ms_blockcnt = 0; spec->ms_blocklen = 0; } if (pd == SPC_PERIPHERAL_DEVICE_TYPE_TAPE || pd == SPC_PERIPHERAL_DEVICE_TYPE_CHANGER) { spec->timeout *= 10; } if (pd == SPC_PERIPHERAL_DEVICE_TYPE_DISK || pd == SPC_PERIPHERAL_DEVICE_TYPE_DVD) { rc = istgt_lu_pass_set_capacity(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_pass_set_capacity() failed\n", lu->num, i); goto error_return; } } else { spec->blockcnt = 0; spec->blocklen = 0; spec->size = 0; } if (spec->ms_blocklen == 0) { if (spec->blocklen == 0) { if (pd == SPC_PERIPHERAL_DEVICE_TYPE_DVD) { spec->ms_blocklen = 2048; } else { spec->ms_blocklen = 512; } } else { spec->ms_blocklen = spec->blocklen; } } if (pd != SPC_PERIPHERAL_DEVICE_TYPE_CHANGER) { printf("LU%d: LUN%d block descriptor\n", lu->num, i); printf("LU%d: LUN%d %"PRIu64" blocks, %"PRIu64" bytes/block\n", lu->num, i, spec->ms_blockcnt, spec->ms_blocklen); if (spec->inq_rmb && spec->blockcnt == 0) { printf("LU%d: LUN%d medium not present\n", lu->num, i); } else { printf("LU%d: LUN%d medium capacity\n", lu->num, i); printf("LU%d: LUN%d %"PRIu64" blocks, %"PRIu64" bytes/block\n", lu->num, i, spec->blockcnt, spec->blocklen); gb_size = spec->size / ISTGT_LU_1GB; mb_size = (spec->size % ISTGT_LU_1GB) / ISTGT_LU_1MB; if (gb_size > 0) { mb_digit = (int) (((mb_size * 100) / 1024) / 10); printf("LU%d: LUN%d %"PRIu64".%dGB\n", lu->num, i, gb_size, mb_digit); } else { printf("LU%d: LUN%d %"PRIu64"MB\n", lu->num, i, mb_size); } } } printf("LU%d: LUN%d %spass through for %s\n", lu->num, i, lu->readonly ? "readonly " : "", lu->name); lu->lun[i].spec = spec; } return 0; } int istgt_lu_pass_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { ISTGT_LU_PASS *spec; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_pass_shutdown\n"); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_DEVICE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } spec = (ISTGT_LU_PASS *) lu->lun[i].spec; if (spec->ccb != NULL) { cam_freeccb(spec->ccb); spec->ccb = NULL; } if (spec->cam_dev != NULL) { cam_close_spec_device(spec->cam_dev); spec->cam_dev = NULL; } if (spec->device != NULL) { xfree(spec->device); spec->device = NULL; } xfree(spec); lu->lun[i].spec = NULL; } return 0; } static int istgt_scsi_get_cdb_len(uint8_t *cdb) { int group; int cdblen = 0; if (cdb == NULL) return 0; group = (cdb[0] >> 5) & 0x07; switch (group) { case 0x00: /* 6byte commands */ cdblen = 6; break; case 0x01: /* 10byte commands */ cdblen = 10; break; case 0x02: /* 10byte commands */ cdblen = 10; break; case 0x03: /* reserved */ if (cdb[0] == 0x7f) { /* variable length */ cdblen = 8 + (cdb[7] & 0xff); } else { /* XXX */ cdblen = 6; } break; case 0x04: /* 16byte commands */ cdblen = 16; break; case 0x05: /* 12byte commands */ cdblen = 12; break; case 0x06: case 0x07: /* vendor specific */ cdblen = 6; break; } return cdblen; } static int istgt_lu_pass_transfer_data(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *buf, size_t bufsize, size_t len) { int rc; if (len > bufsize) { ISTGT_ERRLOG("bufsize(%d) too small\n", bufsize); return -1; } rc = istgt_iscsi_transfer_out(conn, lu_cmd, buf, bufsize, len); if (rc < 0) { ISTGT_ERRLOG("iscsi_transfer_out()\n"); return -1; } return 0; } static int istgt_lu_pass_do_cam(ISTGT_LU_PASS *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { uint32_t flags; uint8_t *cdb; uint8_t *data; int cdb_len; int data_len; int data_alloc_len; uint8_t *sense_data; int *sense_len; int R_bit, W_bit; int transfer_len; int retry = 1; int sk, asc, ascq; int len; int rc; cdb = lu_cmd->cdb; data = lu_cmd->data; data_alloc_len = lu_cmd->alloc_len; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; R_bit = lu_cmd->R_bit; W_bit = lu_cmd->W_bit; transfer_len = lu_cmd->transfer_len; cdb_len = istgt_scsi_get_cdb_len(cdb); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, cdb_len); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); flags = CAM_DIR_NONE; if (R_bit != 0) { flags = CAM_DIR_IN; } else if (W_bit != 0) { flags = CAM_DIR_OUT; } flags |= CAM_DEV_QFRZDIS; cam_fill_csio(&spec->ccb->csio, retry, NULL, flags, MSG_SIMPLE_Q_TAG, data, transfer_len, SSD_FULL_SIZE, cdb_len, spec->timeout); rc = cam_send_ccb(spec->cam_dev, spec->ccb); if (rc < 0) { ISTGT_ERRLOG("cam_send_ccb() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request error CAM=0x%x, SCSI=0x%x\n", spec->ccb->ccb_h.status, spec->ccb->csio.scsi_status); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SENSE", (uint8_t *) &spec->ccb->csio.sense_data, SSD_FULL_SIZE); if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) { memcpy(sense_data + 2, &spec->ccb->csio.sense_data, SSD_FULL_SIZE); DSET16(&sense_data[0], SSD_FULL_SIZE); *sense_len = SSD_FULL_SIZE + 2; lu_cmd->status = spec->ccb->csio.scsi_status; #if 0 if (lu_cmd->status == 0) { /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; } #endif /* adjust fixed format length */ if (BGET8W(&sense_data[2+0], 6, 7) == 0x70 || BGET8W(&sense_data[2+0], 6, 7) == 0x71) { len = DGET8(&sense_data[2+7]); len += 8; if (len < SSD_FULL_SIZE) { *sense_len = len + 2; DSET16(&sense_data[0], len); } } istgt_lu_pass_print_sense_key(sense_data + 2); istgt_lu_pass_parse_sense_key(sense_data + 2, &sk, &asc, &ascq); } else { #if 0 /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; #endif memcpy(sense_data + 2, &spec->ccb->csio.sense_data, SSD_FULL_SIZE); DSET16(&sense_data[0], SSD_FULL_SIZE); *sense_len = SSD_FULL_SIZE + 2; lu_cmd->status = spec->ccb->csio.scsi_status; /* adjust fixed format length */ if (BGET8W(&sense_data[2+0], 6, 7) == 0x70 || BGET8W(&sense_data[2+0], 6, 7) == 0x71) { len = DGET8(&sense_data[2+7]); len += 8; if (len < SSD_FULL_SIZE) { *sense_len = len + 2; DSET16(&sense_data[0], len); } } istgt_lu_pass_print_sense_key(sense_data + 2); istgt_lu_pass_parse_sense_key(sense_data + 2, &sk, &asc, &ascq); } return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "dxfer=%d, resid=%d, sense=%d\n", spec->ccb->csio.dxfer_len, spec->ccb->csio.resid, spec->ccb->csio.sense_resid); data_len = spec->ccb->csio.dxfer_len; data_len -= spec->ccb->csio.resid; if (R_bit != 0 || W_bit != 0) { #if 0 if (data_len > 256) { ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "DOCAM", data, 256); } else { ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "DOCAM", data, data_len); } #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); } else { lu_cmd->data_len = 0; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_pass_do_cam_seg(ISTGT_LU_PASS *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { uint64_t llba; uint32_t lcnt; uint32_t flags; uint8_t fixcdb[16]; uint8_t *cdb; uint8_t *data; int pad_len; int cdb_len; int data_len; int data_alloc_len; uint8_t *sense_data; int *sense_len; int R_bit, W_bit; int transfer_len; int retry = 1; int offset; int seglen; int len, cnt; int sk, asc, ascq; int rc; cdb = lu_cmd->cdb; data = lu_cmd->data; data_alloc_len = lu_cmd->alloc_len; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; R_bit = lu_cmd->R_bit; W_bit = lu_cmd->W_bit; transfer_len = lu_cmd->transfer_len; cdb_len = istgt_scsi_get_cdb_len(cdb); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "CDB", cdb, cdb_len); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, cdb, cdb_len); flags = CAM_DIR_NONE; if (R_bit != 0) { flags = CAM_DIR_IN; } else if (W_bit != 0) { flags = CAM_DIR_OUT; } flags |= CAM_DEV_QFRZDIS; //#define MAX_SEGLEN (65536-4096) #define MAX_SEGLEN (65536) pad_len = (int) ((uintptr_t) data & (4096 - 1)); if (pad_len != 0) { pad_len = 4096 - pad_len; data += pad_len; data_alloc_len -= pad_len; } data_len = 0; seglen = MAX_SEGLEN; seglen -= MAX_SEGLEN % (int) spec->ms_blocklen; len = 0; for (offset = 0; offset < transfer_len; offset += seglen) { len = DMIN32(seglen, (transfer_len - offset)); cnt = len / (int) spec->ms_blocklen; switch(cdb[0]) { case SBC_READ_6: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET16(&cdb[2]); lcnt = (uint32_t) DGET8(&cdb[4]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET16(&fixcdb[2], (uint16_t) llba); DSET8(&fixcdb[4], (uint8_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; case SBC_READ_10: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET32(&cdb[2]); lcnt = (uint32_t) DGET16(&cdb[7]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET32(&fixcdb[2], (uint32_t) llba); DSET16(&fixcdb[7], (uint16_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; case SBC_READ_12: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET32(&cdb[2]); lcnt = (uint32_t) DGET32(&cdb[6]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET32(&fixcdb[2], (uint32_t) llba); DSET32(&fixcdb[6], (uint32_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; case SBC_READ_16: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET64(&cdb[2]); lcnt = (uint32_t) DGET32(&cdb[10]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET64(&fixcdb[2], (uint64_t) llba); DSET32(&fixcdb[10], (uint32_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; case SBC_WRITE_6: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET16(&cdb[2]); lcnt = (uint32_t) DGET8(&cdb[4]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET16(&fixcdb[2], (uint16_t) llba); DSET8(&fixcdb[4], (uint8_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; case SBC_WRITE_10: case SBC_WRITE_AND_VERIFY_10: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET32(&cdb[2]); lcnt = (uint32_t) DGET16(&cdb[7]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET32(&fixcdb[2], (uint32_t) llba); DSET16(&fixcdb[7], (uint16_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; case SBC_WRITE_12: case SBC_WRITE_AND_VERIFY_12: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET32(&cdb[2]); lcnt = (uint32_t) DGET32(&cdb[6]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET32(&fixcdb[2], (uint32_t) llba); DSET32(&fixcdb[6], (uint32_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; case SBC_WRITE_16: case SBC_WRITE_AND_VERIFY_16: memcpy(fixcdb, cdb, cdb_len); llba = (uint64_t) DGET64(&cdb[2]); lcnt = (uint32_t) DGET32(&cdb[10]); llba += offset / spec->ms_blocklen; lcnt = (uint64_t) cnt; DSET64(&fixcdb[2], (uint64_t) llba); DSET32(&fixcdb[10], (uint32_t) lcnt); memcpy(spec->ccb->csio.cdb_io.cdb_bytes, fixcdb, cdb_len); break; default: ISTGT_ERRLOG("unsupported OP=0x%x\n", cdb[0]); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } cam_fill_csio(&spec->ccb->csio, retry, NULL, flags, MSG_SIMPLE_Q_TAG, data + offset, len, SSD_FULL_SIZE, cdb_len, spec->timeout); rc = cam_send_ccb(spec->cam_dev, spec->ccb); if (rc < 0) { ISTGT_ERRLOG("cam_send_ccb() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "request error CAM=0x%x, SCSI=0x%x\n", spec->ccb->ccb_h.status, spec->ccb->csio.scsi_status); ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "SENSE", (uint8_t *) &spec->ccb->csio.sense_data, SSD_FULL_SIZE); if ((spec->ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR) { memcpy(sense_data + 2, &spec->ccb->csio.sense_data, SSD_FULL_SIZE); DSET16(&sense_data[0], SSD_FULL_SIZE); *sense_len = SSD_FULL_SIZE + 2; lu_cmd->status = spec->ccb->csio.scsi_status; #if 0 if (lu_cmd->status == 0) { /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; } #endif /* adjust fixed format length */ if (BGET8W(&sense_data[2+0], 6, 7) == 0x70 || BGET8W(&sense_data[2+0], 6, 7) == 0x71) { len = DGET8(&sense_data[2+7]); len += 8; if (len < SSD_FULL_SIZE) { *sense_len = len + 2; DSET16(&sense_data[0], len); } } istgt_lu_pass_print_sense_key(sense_data + 2); istgt_lu_pass_parse_sense_key(sense_data + 2, &sk, &asc, &ascq); } else { #if 0 /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; #endif memcpy(sense_data + 2, &spec->ccb->csio.sense_data, SSD_FULL_SIZE); DSET16(&sense_data[0], SSD_FULL_SIZE); *sense_len = SSD_FULL_SIZE + 2; lu_cmd->status = spec->ccb->csio.scsi_status; /* adjust fixed format length */ if (BGET8W(&sense_data[2+0], 6, 7) == 0x70 || BGET8W(&sense_data[2+0], 6, 7) == 0x71) { len = DGET8(&sense_data[2+7]); len += 8; if (len < SSD_FULL_SIZE) { *sense_len = len + 2; DSET16(&sense_data[0], len); } } istgt_lu_pass_print_sense_key(sense_data + 2); istgt_lu_pass_parse_sense_key(sense_data + 2, &sk, &asc, &ascq); } return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "dxfer=%d, resid=%d, sense=%d\n", spec->ccb->csio.dxfer_len, spec->ccb->csio.resid, spec->ccb->csio.sense_resid); if (spec->ccb->csio.resid != 0) { /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len += spec->ccb->csio.dxfer_len; data_len -= spec->ccb->csio.resid; } if (pad_len != 0) { memcpy(lu_cmd->data, lu_cmd->data + pad_len, data_len); } if (R_bit !=0 || W_bit != 0) { #if 0 if (data_len > 256) { ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "DOCAM", data, 256); } else { ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "DOCAM", data, data_len); } #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); } else { lu_cmd->data_len = 0; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_pass_build_sense_data(ISTGT_LU_PASS *spec, uint8_t *data, int sk, int asc, int ascq) { int rc; rc = istgt_lu_scsi_build_sense_data(data, sk, asc, ascq); if (rc < 0) { return -1; } return rc; } int istgt_lu_pass_reset(ISTGT_LU_Ptr lu, int lun) { ISTGT_LU_PASS *spec; if (lun >= lu->maxlun) { return -1; } if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) { return -1; } spec = (ISTGT_LU_PASS *) lu->lun[lun].spec; #if 0 if (spec->lock) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "unlock by reset\n"); spec->lock = 0; } #endif return 0; } int istgt_lu_pass_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_Ptr lu; ISTGT_LU_PASS *spec; uint8_t *data; uint8_t *cdb; uint64_t fmt_lun; uint64_t lun; uint64_t method; uint32_t allocation_len; int data_len; int data_alloc_len; uint32_t transfer_len; uint8_t *sense_data; int *sense_len; int rc; if (lu_cmd == NULL) return -1; lu = lu_cmd->lu; if (lu == NULL) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } spec = NULL; cdb = lu_cmd->cdb; data = lu_cmd->data; data_alloc_len = lu_cmd->alloc_len; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; fmt_lun = lu_cmd->lun; method = (fmt_lun >> 62) & 0x03U; fmt_lun = fmt_lun >> 48; if (method == 0x00U) { lun = fmt_lun & 0x00ffU; } else if (method == 0x01U) { lun = fmt_lun & 0x3fffU; } else { lun = 0xffffU; } if (lun >= lu->maxlun) { #ifdef ISTGT_TRACE_PASS ISTGT_ERRLOG("LU%d: LUN%4.4"PRIx64" invalid\n", lu->num, lun); #endif /* ISTGT_TRACE_PASS */ if (cdb[0] == SPC_INQUIRY) { allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], 0x03, 7, 3); BDADD8W(&data[0], 0x1f, 4, 5); data_len = 96; memset(&data[1], 0, data_len - 1); /* ADDITIONAL LENGTH */ data[4] = data_len - 5; lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } else { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } } spec = (ISTGT_LU_PASS *) lu->lun[lun].spec; if (spec == NULL) { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64"\n", cdb[0], lu_cmd->lun); #ifdef ISTGT_TRACE_PASS if (cdb[0] != SPC_TEST_UNIT_READY) { istgt_scsi_dump_cdb(cdb); } #endif /* ISTGT_TRACE_DISK */ if (lu_cmd->W_bit != 0) { transfer_len = lu_cmd->transfer_len; rc = istgt_lu_pass_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, transfer_len); if (rc < 0) { ISTGT_ERRLOG("lu_pass_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lu_cmd->data = lu_cmd->iobuf; lu_cmd->alloc_len = lu_cmd->iobufsize; } switch (spec->inq_pd) { case SPC_PERIPHERAL_DEVICE_TYPE_DISK: switch (cdb[0]) { case SBC_READ_6: case SBC_READ_10: case SBC_READ_12: case SBC_READ_16: case SBC_WRITE_6: case SBC_WRITE_12: case SBC_WRITE_AND_VERIFY_12: case SBC_WRITE_10: case SBC_WRITE_AND_VERIFY_10: case SBC_WRITE_16: case SBC_WRITE_AND_VERIFY_16: lu_cmd->data = lu_cmd->iobuf; lu_cmd->alloc_len = lu_cmd->iobufsize; if (lu_cmd->transfer_len > lu_cmd->alloc_len) { ISTGT_ERRLOG("alloc_len(%d) too small\n", lu_cmd->alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } rc = istgt_lu_pass_do_cam_seg(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; default: rc = istgt_lu_pass_do_cam(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } break; case SPC_PERIPHERAL_DEVICE_TYPE_TAPE: switch (cdb[0]) { default: rc = istgt_lu_pass_do_cam(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } break; case SPC_PERIPHERAL_DEVICE_TYPE_DVD: switch (cdb[0]) { case MMC_READ_10: case MMC_READ_12: case MMC_WRITE_10: case MMC_WRITE_AND_VERIFY_10: case MMC_WRITE_12: lu_cmd->data = lu_cmd->iobuf; lu_cmd->alloc_len = lu_cmd->iobufsize; if (lu_cmd->transfer_len > lu_cmd->alloc_len) { ISTGT_ERRLOG("alloc_len(%d) too small\n", lu_cmd->alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } rc = istgt_lu_pass_do_cam_seg(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; #ifdef ISTGT_TRACE_PASS case MMC_GET_EVENT_STATUS_NOTIFICATION: rc = istgt_lu_pass_do_cam(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "EVENT", lu_cmd->data, lu_cmd->data_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; #endif /* ISTGT_TRACE_PASS */ default: rc = istgt_lu_pass_do_cam(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } break; case SPC_PERIPHERAL_DEVICE_TYPE_CHANGER: switch (cdb[0]) { default: rc = istgt_lu_pass_do_cam(spec, conn, lu_cmd); if (rc < 0) { /* build by function */ break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } break; default: ISTGT_ERRLOG("unsupported peripheral device type (%x)\n", spec->inq_pd); /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64" status=0x%x," " complete\n", cdb[0], lu_cmd->lun, lu_cmd->status); return 0; } #else /* HAVE_LIBCAM */ #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_misc.h" #include "istgt_lu.h" #include "istgt_proto.h" #include "istgt_scsi.h" int istgt_lu_pass_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { return 0; } int istgt_lu_pass_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { return 0; } int istgt_lu_pass_reset(ISTGT_LU_Ptr lu, int lun) { return 0; } int istgt_lu_pass_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "unsupported unit\n"); return -1; } #endif /* HAVE_LIBCAM */ istgt-20111008/src/istgt_log.h000644 000000 000000 00000007136 11317704406 016146 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_LOG_H #define ISTGT_LOG_H #include #include #ifndef ISTGT_LOG_FACILITY #define ISTGT_LOG_FACILITY LOG_LOCAL7 #endif #ifndef ISTGT_LOG_PRIORITY #define ISTGT_LOG_PRIORITY LOG_NOTICE #endif #define ISTGT_TRACE_ALL (~0U) #define ISTGT_TRACE_NONE 0U #define ISTGT_TRACE_DEBUG 0x80000000U #define ISTGT_TRACE_NET 0x0000000fU #define ISTGT_TRACE_ISCSI 0x000000f0U #define ISTGT_TRACE_SCSI 0x00000f00U #define ISTGT_TRACE_LU 0x0000f000U #define ISTGT_LOG(...) \ istgt_log(NULL, 0, NULL, __VA_ARGS__) #define ISTGT_NOTICELOG(...) \ istgt_noticelog(NULL, 0, NULL, __VA_ARGS__) #define ISTGT_ERRLOG(...) \ istgt_errlog(__FILE__, __LINE__, __func__, __VA_ARGS__) #define ISTGT_WARNLOG(...) \ do { \ if (g_warn_flag != 0) { \ istgt_warnlog(__FILE__, __LINE__, __func__, __VA_ARGS__); \ } \ } while (0) #ifdef DEBUG #define ISTGT_TRACELOG(FLAG, ...) \ do { \ if (g_trace_flag & (FLAG)) { \ istgt_tracelog((FLAG), __FILE__, __LINE__, __func__, \ __VA_ARGS__); \ } \ } while (0) #define ISTGT_TRACEDUMP(FLAG, LABEL, BUF, LEN) \ do { \ if (g_trace_flag & (FLAG)) { \ istgt_trace_dump((FLAG), (LABEL), (BUF), (LEN)); \ } \ } while (0) #else #define ISTGT_TRACELOG(FLAG, ...) #define ISTGT_TRACEDUMP(FLAG, LABEL, BUF, LEN) #endif /* DEBUG */ int istgt_set_log_facility(const char *facility); int istgt_set_log_priority(const char *priority); void istgt_log(const char *file, const int line, const char *func, const char *format, ...); void istgt_noticelog(const char *file, const int line, const char *func, const char *format, ...); void istgt_tracelog(const int flag, const char *file, const int line, const char *func, const char *format, ...); void istgt_errlog(const char *file, const int line, const char *func, const char *format, ...); void istgt_warnlog(const char *file, const int line, const char *func, const char *format, ...); void istgt_open_log(void); void istgt_close_log(void); void istgt_set_trace_flag(int flag); void istgt_trace_dump(int flag, const char *label, const uint8_t *buf, size_t len); extern int g_trace_flag; extern int g_warn_flag; #endif /* ISTGT_LOG_H */ istgt-20111008/src/istgt.c000644 000000 000000 00000130373 11640373417 015303 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_PTHREAD_NP_H #include #endif #include #include #include #include #include #include #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_crc32c.h" #include "istgt_iscsi.h" #include "istgt_lu.h" #include "istgt_proto.h" #ifdef ISTGT_USE_KQUEUE #include #include #include #endif #define POLLWAIT 3000 #define PORTNUMLEN 32 ISTGT g_istgt; #if 0 void fatal(const char *msg, ...) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); exit(EXIT_FAILURE); } #endif static int istgt_parse_portal(const char *portal, char **host, char **port) { const char *p; int n; if (portal == NULL) { ISTGT_ERRLOG("portal error\n"); return -1; } if (portal[0] == '[') { /* IPv6 */ p = strchr(portal + 1, ']'); if (p == NULL) { ISTGT_ERRLOG("portal error\n"); return -1; } #if 0 n = p - (portal + 1); *host = xmalloc(n + 1); memcpy(*host, portal + 1, n); (*host)[n] = '\0'; p++; #else p++; n = p - portal; *host = xmalloc(n + 1); memcpy(*host, portal, n); (*host)[n] = '\0'; #endif if (p[0] == '\0') { *port = xmalloc(PORTNUMLEN); snprintf(*port, PORTNUMLEN, "%d", DEFAULT_PORT); } else { if (p[0] != ':') { ISTGT_ERRLOG("portal error\n"); xfree(*host); return -1; } *port = xstrdup(p + 1); } } else { /* IPv4 */ p = strchr(portal, ':'); if (p == NULL) { p = portal + strlen(portal); } n = p - portal; *host = xmalloc(n + 1); memcpy(*host, portal, n); (*host)[n] = '\0'; if (p[0] == '\0') { *port = xmalloc(PORTNUMLEN); snprintf(*port, PORTNUMLEN, "%d", DEFAULT_PORT); } else { if (p[0] != ':') { ISTGT_ERRLOG("portal error\n"); xfree(*host); return -1; } *port = xstrdup(p + 1); } } return 0; } static int istgt_add_portal(ISTGT_Ptr istgt, CF_SECTION *sp, int idx1) { char *label, *portal, *host, *port; int idx; int rc; label = istgt_get_nmval(sp, "Portal", idx1, 0); portal = istgt_get_nmval(sp, "Portal", idx1, 1); if (label == NULL || portal == NULL) { ISTGT_ERRLOG("portal error\n"); return -1; } rc = istgt_parse_portal(portal, &host, &port); if (rc < 0) { ISTGT_ERRLOG("parse portal error\n"); return -1; } idx = istgt->nportal; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Host=%s, Port=%s, Tag=%d\n", idx, host, port, sp->num); if (idx < MAX_PORTAL) { istgt->portal[idx].label = xstrdup(label); istgt->portal[idx].host = host; istgt->portal[idx].port = port; istgt->portal[idx].idx = idx; istgt->portal[idx].tag = sp->num; istgt->portal[idx].sock = -1; idx++; istgt->nportal = idx; } else { ISTGT_ERRLOG("nportal(%d) >= MAX_PORTAL\n", idx); xfree(host); xfree(port); return -1; } return 0; } static int istgt_build_portal_array(ISTGT_Ptr istgt) { CF_SECTION *sp; const char *val; int rc; int i; sp = istgt->config->section; while (sp != NULL) { if (sp->type == ST_PORTALGROUP) { if (sp->num == 0) { ISTGT_ERRLOG("Group 0 is invalid\n"); return -1; } if (sp->num > ISTGT_PG_TAG_MAX) { ISTGT_ERRLOG("tag %d is invalid\n", sp->num); return -1; } val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Portal", i); if (val == NULL) break; rc = istgt_add_portal(istgt, sp, i); if (rc < 0) { ISTGT_ERRLOG("add_portal() failed\n"); return -1; } } } sp = sp->next; } return 0; } static void istgt_destroy_portal_array(ISTGT_Ptr istgt) { int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_destroy_portal_array\n"); for (i = 0; i < istgt->nportal; i++) { xfree(istgt->portal[i].label); xfree(istgt->portal[i].host); xfree(istgt->portal[i].port); istgt->portal[i].label = NULL; istgt->portal[i].host = NULL; istgt->portal[i].port = NULL; istgt->portal[i].idx = i; istgt->portal[i].tag = 0; } istgt->nportal = 0; } static int istgt_open_portal(ISTGT_Ptr istgt) { int port; int sock; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_open_portal\n"); for (i = 0; i < istgt->nportal; i++) { if (istgt->portal[i].sock < 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "open host %s, port %s, tag %d\n", istgt->portal[i].host, istgt->portal[i].port, istgt->portal[i].tag); port = (int)strtol(istgt->portal[i].port, NULL, 0); sock = istgt_listen(istgt->portal[i].host, port); if (sock < 0) { ISTGT_ERRLOG("listen error %.64s:%d\n", istgt->portal[i].host, port); return -1; } istgt->portal[i].sock = sock; } } return 0; } static int istgt_close_portal(ISTGT_Ptr istgt) { int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_close_portal\n"); for (i = 0; i < istgt->nportal; i++) { if (istgt->portal[i].sock >= 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "close host %s, port %s, tag %d\n", istgt->portal[i].host, istgt->portal[i].port, istgt->portal[i].tag); close(istgt->portal[i].sock); istgt->portal[i].sock = -1; } } return 0; } static int istgt_add_initiator_group(ISTGT_Ptr istgt, CF_SECTION *sp) { const char *val; int alloc_len; int idx; int names; int masks; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "add initiator group %d\n", sp->num); val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } /* counts number of definition */ for (i = 0; ; i++) { val = istgt_get_nval(sp, "InitiatorName", i); if (val == NULL) break; } names = i; if (names > MAX_INITIATOR) { ISTGT_ERRLOG("%d > MAX_INITIATOR\n", names); return -1; } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Netmask", i); if (val == NULL) break; } masks = i; if (masks > MAX_NETMASK) { ISTGT_ERRLOG("%d > MAX_NETMASK\n", masks); return -1; } idx = istgt->ninitiator_group; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Tag=%d, Names=%d, Masks=%d\n", idx, sp->num, names, masks); if (idx < MAX_INITIATOR_GROUP) { istgt->initiator_group[idx].ninitiators = names; alloc_len = sizeof (char *) * names; istgt->initiator_group[idx].initiators = xmalloc(alloc_len); istgt->initiator_group[idx].nnetmasks = masks; alloc_len = sizeof (char *) * masks; istgt->initiator_group[idx].netmasks = xmalloc(alloc_len); istgt->initiator_group[idx].idx = idx; istgt->initiator_group[idx].tag = sp->num; for (i = 0; i < names; i++) { val = istgt_get_nval(sp, "InitiatorName", i); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "InitiatorName %s\n", val); istgt->initiator_group[idx].initiators[i] = xstrdup(val); } for (i = 0; i < masks; i++) { val = istgt_get_nval(sp, "Netmask", i); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Netmask %s\n", val); istgt->initiator_group[idx].netmasks[i] = xstrdup(val); } idx++; istgt->ninitiator_group = idx; } else { ISTGT_ERRLOG("ninitiator_group(%d) >= MAX_INITIATOR_GROUP\n", idx); return -1; } return 0; } static int istgt_build_initiator_group_array(ISTGT_Ptr istgt) { CF_SECTION *sp; int rc; sp = istgt->config->section; while (sp != NULL) { if (sp->type == ST_INITIATORGROUP) { if (sp->num == 0) { ISTGT_ERRLOG("Group 0 is invalid\n"); return -1; } rc = istgt_add_initiator_group(istgt, sp); if (rc < 0) { ISTGT_ERRLOG("add_initiator_group() failed\n"); return -1; } } sp = sp->next; } return 0; } static void istgt_destory_initiator_group_array(ISTGT_Ptr istgt) { int i, j; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_destory_initiator_group_array\n"); for (i = 0; i < istgt->ninitiator_group; i++) { for (j = 0; j < istgt->initiator_group[i].ninitiators; j++) { xfree(istgt->initiator_group[i].initiators[j]); } xfree(istgt->initiator_group[i].initiators); for (j = 0; j < istgt->initiator_group[i].nnetmasks; j++) { xfree(istgt->initiator_group[i].netmasks[j]); } xfree(istgt->initiator_group[i].netmasks); istgt->initiator_group[i].ninitiators = 0; istgt->initiator_group[i].initiators = NULL; istgt->initiator_group[i].nnetmasks = 0; istgt->initiator_group[i].netmasks = NULL; istgt->initiator_group[i].idx = i; istgt->initiator_group[i].tag = 0; } istgt->ninitiator_group = 0; } static int istgt_build_uctl_portal(ISTGT_Ptr istgt) { CF_SECTION *sp; const char *val; char *label, *portal, *host, *port; int tag; int idx; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_build_uctl_portal\n"); sp = istgt_find_cf_section(istgt->config, "UnitControl"); if (sp == NULL) { ISTGT_ERRLOG("find_cf_section failed()\n"); return -1; } for (i = 0; ; i++) { val = istgt_get_nval(sp, "Portal", i); if (val == NULL) break; label = istgt_get_nmval(sp, "Portal", i, 0); portal = istgt_get_nmval(sp, "Portal", i, 1); if (label == NULL || portal == NULL) { ISTGT_ERRLOG("uctl portal error\n"); return -1; } rc = istgt_parse_portal(portal, &host, &port); if (rc < 0) { ISTGT_ERRLOG("parse uctl portal error\n"); return -1; } idx = istgt->nuctl_portal; tag = ISTGT_UC_TAG; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Index=%d, Host=%s, Port=%s, Tag=%d\n", idx, host, port, tag); if (idx < MAX_UCPORTAL) { istgt->uctl_portal[idx].label = xstrdup(label); istgt->uctl_portal[idx].host = host; istgt->uctl_portal[idx].port = port; istgt->uctl_portal[idx].idx = idx; istgt->uctl_portal[idx].tag = tag; istgt->uctl_portal[idx].sock = -1; idx++; istgt->nuctl_portal = idx; } else { ISTGT_ERRLOG("nportal(%d) >= MAX_UCPORTAL\n", idx); xfree(host); xfree(port); return -1; } } return 0; } static void istgt_destroy_uctl_portal(ISTGT_Ptr istgt) { int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_destroy_uctl_portal\n"); for (i = 0; i < istgt->nportal; i++) { xfree(istgt->uctl_portal[i].label); xfree(istgt->uctl_portal[i].host); xfree(istgt->uctl_portal[i].port); istgt->uctl_portal[i].label = NULL; istgt->uctl_portal[i].host = NULL; istgt->uctl_portal[i].port = NULL; istgt->uctl_portal[i].idx = i; istgt->uctl_portal[i].tag = 0; } istgt->nuctl_portal = 0; } static int istgt_open_uctl_portal(ISTGT_Ptr istgt) { int port; int sock; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_open_uctl_portal\n"); for (i = 0; i < istgt->nuctl_portal; i++) { if (istgt->uctl_portal[i].sock < 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "open host %s, port %s, tag %d\n", istgt->uctl_portal[i].host, istgt->uctl_portal[i].port, istgt->uctl_portal[i].tag); port = (int)strtol(istgt->uctl_portal[i].port, NULL, 0); sock = istgt_listen(istgt->uctl_portal[i].host, port); if (sock < 0) { ISTGT_ERRLOG("listen error %.64s:%d\n", istgt->uctl_portal[i].host, port); return -1; } istgt->uctl_portal[i].sock = sock; } } return 0; } static int istgt_close_uctl_portal(ISTGT_Ptr istgt) { int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_close_uctl_portal\n"); for (i = 0; i < istgt->nuctl_portal; i++) { if (istgt->uctl_portal[i].sock >= 0) { ISTGT_TRACELOG(ISTGT_TRACE_NET, "close host %s, port %s, tag %d\n", istgt->uctl_portal[i].host, istgt->uctl_portal[i].port, istgt->uctl_portal[i].tag); close(istgt->uctl_portal[i].sock); istgt->uctl_portal[i].sock = -1; } } return 0; } static int istgt_write_pidfile(ISTGT_Ptr istgt) { FILE *fp; pid_t pid; int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_write_pidfile\n"); rc = remove(istgt->pidfile); if (rc != 0) { if (errno != ENOENT) { ISTGT_ERRLOG("pidfile remove error %d\n", errno); return -1; } } fp = fopen(istgt->pidfile, "w"); if (fp == NULL) { ISTGT_ERRLOG("pidfile open error %d\n", errno); return -1; } pid = getpid(); fprintf(fp, "%d\n", (int)pid); fclose(fp); return 0; } static void istgt_remove_pidfile(ISTGT_Ptr istgt) { int rc; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_remove_pidfile\n"); rc = remove(istgt->pidfile); if (rc != 0) { ISTGT_ERRLOG("pidfile remove error %d\n", errno); /* ignore error */ } } char * istgt_get_nmval(CF_SECTION *sp, const char *key, int idx1, int idx2) { CF_ITEM *ip; CF_VALUE *vp; int i; ip = istgt_find_cf_nitem(sp, key, idx1); if (ip == NULL) return NULL; vp = ip->val; if (vp == NULL) return NULL; for (i = 0; vp != NULL; vp = vp->next) { if (i == idx2) return vp->value; i++; } return NULL; } char * istgt_get_nval(CF_SECTION *sp, const char *key, int idx) { CF_ITEM *ip; CF_VALUE *vp; ip = istgt_find_cf_nitem(sp, key, idx); if (ip == NULL) return NULL; vp = ip->val; if (vp == NULL) return NULL; return vp->value; } char * istgt_get_val(CF_SECTION *sp, const char *key) { return istgt_get_nval(sp, key, 0); } int istgt_get_nintval(CF_SECTION *sp, const char *key, int idx) { const char *v; int value; v = istgt_get_nval(sp, key, idx); if (v == NULL) return -1; value = (int)strtol(v, NULL, 10); return value; } int istgt_get_intval(CF_SECTION *sp, const char *key) { return istgt_get_nintval(sp, key, 0); } static const char * istgt_get_log_facility(CONFIG *config) { CF_SECTION *sp; const char *logfacility; sp = istgt_find_cf_section(config, "Global"); if (sp == NULL) { return NULL; } logfacility = istgt_get_val(sp, "LogFacility"); if (logfacility == NULL) { logfacility = DEFAULT_LOG_FACILITY; } #if 0 if (g_trace_flag & ISTGT_TRACE_DEBUG) { fprintf(stderr, "LogFacility %s\n", logfacility); } #endif return logfacility; } static int istgt_init(ISTGT_Ptr istgt) { CF_SECTION *sp; const char *ag_tag; const char *val; size_t stacksize; int ag_tag_i; int MaxSessions; int MaxConnections; int MaxOutstandingR2T; int DefaultTime2Wait; int DefaultTime2Retain; int FirstBurstLength; int MaxBurstLength; int MaxRecvDataSegmentLength; int InitialR2T; int ImmediateData; int DataPDUInOrder; int DataSequenceInOrder; int ErrorRecoveryLevel; int timeout; int nopininterval; int maxr2t; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_init\n"); sp = istgt_find_cf_section(istgt->config, "Global"); if (sp == NULL) { ISTGT_ERRLOG("find_cf_section failed()\n"); return -1; } val = istgt_get_val(sp, "Comment"); if (val != NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Comment %s\n", val); } val = istgt_get_val(sp, "PidFile"); if (val == NULL) { val = DEFAULT_PIDFILE; } istgt->pidfile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "PidFile %s\n", istgt->pidfile); val = istgt_get_val(sp, "AuthFile"); if (val == NULL) { val = DEFAULT_AUTHFILE; } istgt->authfile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "AuthFile %s\n", istgt->authfile); #if 0 val = istgt_get_val(sp, "MediaFile"); if (val == NULL) { val = DEFAULT_MEDIAFILE; } istgt->mediafile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MediaFile %s\n", istgt->mediafile); #endif #if 0 val = istgt_get_val(sp, "LiveFile"); if (val == NULL) { val = DEFAULT_LIVEFILE; } istgt->livefile = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LiveFile %s\n", istgt->livefile); #endif val = istgt_get_val(sp, "MediaDirectory"); if (val == NULL) { val = DEFAULT_MEDIADIRECTORY; } istgt->mediadirectory = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MediaDirectory %s\n", istgt->mediadirectory); val = istgt_get_val(sp, "NodeBase"); if (val == NULL) { val = DEFAULT_NODEBASE; } istgt->nodebase = xstrdup(val); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "NodeBase %s\n", istgt->nodebase); MaxSessions = istgt_get_intval(sp, "MaxSessions"); if (MaxSessions < 1) { MaxSessions = DEFAULT_MAX_SESSIONS; } istgt->MaxSessions = MaxSessions; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxSessions %d\n", istgt->MaxSessions); MaxConnections = istgt_get_intval(sp, "MaxConnections"); if (MaxConnections < 1) { MaxConnections = DEFAULT_MAX_CONNECTIONS; } istgt->MaxConnections = MaxConnections; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxConnections %d\n", istgt->MaxConnections); /* limited to 16bits - RFC3720(12.2) */ if (MaxSessions > 0xffff) { ISTGT_ERRLOG("over 65535 sessions are not supported\n"); return -1; } if (MaxConnections > 0xffff) { ISTGT_ERRLOG("over 65535 connections are not supported\n"); return -1; } MaxOutstandingR2T = istgt_get_intval(sp, "MaxOutstandingR2T"); if (MaxOutstandingR2T < 1) { MaxOutstandingR2T = DEFAULT_MAXOUTSTANDINGR2T; } istgt->MaxOutstandingR2T = MaxOutstandingR2T; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxOutstandingR2T %d\n", istgt->MaxOutstandingR2T); DefaultTime2Wait = istgt_get_intval(sp, "DefaultTime2Wait"); if (DefaultTime2Wait < 0) { DefaultTime2Wait = DEFAULT_DEFAULTTIME2WAIT; } istgt->DefaultTime2Wait = DefaultTime2Wait; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DefaultTime2Wait %d\n", istgt->DefaultTime2Wait); DefaultTime2Retain = istgt_get_intval(sp, "DefaultTime2Retain"); if (DefaultTime2Retain < 0) { DefaultTime2Retain = DEFAULT_DEFAULTTIME2RETAIN; } istgt->DefaultTime2Retain = DefaultTime2Retain; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DefaultTime2Retain %d\n", istgt->DefaultTime2Retain); /* check size limit - RFC3720(12.15, 12.16, 12.17) */ if (istgt->MaxOutstandingR2T > 65535) { ISTGT_ERRLOG("MaxOutstandingR2T(%d) > 65535\n", istgt->MaxOutstandingR2T); return -1; } if (istgt->DefaultTime2Wait > 3600) { ISTGT_ERRLOG("DefaultTime2Wait(%d) > 3600\n", istgt->DefaultTime2Wait); return -1; } if (istgt->DefaultTime2Retain > 3600) { ISTGT_ERRLOG("DefaultTime2Retain(%d) > 3600\n", istgt->DefaultTime2Retain); return -1; } FirstBurstLength = istgt_get_intval(sp, "FirstBurstLength"); if (FirstBurstLength < 0) { FirstBurstLength = DEFAULT_FIRSTBURSTLENGTH; } istgt->FirstBurstLength = FirstBurstLength; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "FirstBurstLength %d\n", istgt->FirstBurstLength); MaxBurstLength = istgt_get_intval(sp, "MaxBurstLength"); if (MaxBurstLength < 0) { MaxBurstLength = DEFAULT_MAXBURSTLENGTH; } istgt->MaxBurstLength = MaxBurstLength; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxBurstLength %d\n", istgt->MaxBurstLength); MaxRecvDataSegmentLength = istgt_get_intval(sp, "MaxRecvDataSegmentLength"); if (MaxRecvDataSegmentLength < 0) { MaxRecvDataSegmentLength = DEFAULT_MAXRECVDATASEGMENTLENGTH; } istgt->MaxRecvDataSegmentLength = MaxRecvDataSegmentLength; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxRecvDataSegmentLength %d\n", istgt->MaxRecvDataSegmentLength); /* check size limit (up to 24bits - RFC3720(12.12)) */ if (istgt->MaxBurstLength < 512) { ISTGT_ERRLOG("MaxBurstLength(%d) < 512\n", istgt->MaxBurstLength); return -1; } if (istgt->FirstBurstLength < 512) { ISTGT_ERRLOG("FirstBurstLength(%d) < 512\n", istgt->FirstBurstLength); return -1; } if (istgt->FirstBurstLength > istgt->MaxBurstLength) { ISTGT_ERRLOG("FirstBurstLength(%d) > MaxBurstLength(%d)\n", istgt->FirstBurstLength, istgt->MaxBurstLength); return -1; } if (istgt->MaxBurstLength > 0x00ffffff) { ISTGT_ERRLOG("MaxBurstLength(%d) > 0x00ffffff\n", istgt->MaxBurstLength); return -1; } if (istgt->MaxRecvDataSegmentLength < 512) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) < 512\n", istgt->MaxRecvDataSegmentLength); return -1; } if (istgt->MaxRecvDataSegmentLength > 0x00ffffff) { ISTGT_ERRLOG("MaxRecvDataSegmentLength(%d) > 0x00ffffff\n", istgt->MaxRecvDataSegmentLength); return -1; } val = istgt_get_val(sp, "InitialR2T"); if (val == NULL) { InitialR2T = DEFAULT_INITIALR2T; } else if (strcasecmp(val, "Yes") == 0) { InitialR2T = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 InitialR2T = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->InitialR2T = InitialR2T; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "InitialR2T %s\n", istgt->InitialR2T ? "Yes" : "No"); val = istgt_get_val(sp, "ImmediateData"); if (val == NULL) { ImmediateData = DEFAULT_IMMEDIATEDATA; } else if (strcasecmp(val, "Yes") == 0) { ImmediateData = 1; } else if (strcasecmp(val, "No") == 0) { ImmediateData = 0; } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->ImmediateData = ImmediateData; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ImmediateData %s\n", istgt->ImmediateData ? "Yes" : "No"); val = istgt_get_val(sp, "DataPDUInOrder"); if (val == NULL) { DataPDUInOrder = DEFAULT_DATAPDUINORDER; } else if (strcasecmp(val, "Yes") == 0) { DataPDUInOrder = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 DataPDUInOrder = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->DataPDUInOrder = DataPDUInOrder; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DataPDUInOrder %s\n", istgt->DataPDUInOrder ? "Yes" : "No"); val = istgt_get_val(sp, "DataSequenceInOrder"); if (val == NULL) { DataSequenceInOrder = DEFAULT_DATASEQUENCEINORDER; } else if (strcasecmp(val, "Yes") == 0) { DataSequenceInOrder = 1; } else if (strcasecmp(val, "No") == 0) { #if 0 DataSequenceInOrder = 0; #else ISTGT_ERRLOG("not supported value %s\n", val); return -1; #endif } else { ISTGT_ERRLOG("unknown value %s\n", val); return -1; } istgt->DataSequenceInOrder = DataSequenceInOrder; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DataSequenceInOrder %s\n", istgt->DataSequenceInOrder ? "Yes" : "No"); ErrorRecoveryLevel = istgt_get_intval(sp, "ErrorRecoveryLevel"); if (ErrorRecoveryLevel < 0) { ErrorRecoveryLevel = DEFAULT_ERRORRECOVERYLEVEL; } else if (ErrorRecoveryLevel == 0) { ErrorRecoveryLevel = 0; } else if (ErrorRecoveryLevel == 1) { #if 0 ErrorRecoveryLevel = 1; #else ISTGT_ERRLOG("not supported value %d\n", ErrorRecoveryLevel); return -1; #endif } else if (ErrorRecoveryLevel == 2) { #if 0 ErrorRecoveryLevel = 2; #else ISTGT_ERRLOG("not supported value %d\n", ErrorRecoveryLevel); return -1; #endif } else { ISTGT_ERRLOG("not supported value %d\n", ErrorRecoveryLevel); return -1; } istgt->ErrorRecoveryLevel = ErrorRecoveryLevel; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "ErrorRecoveryLevel %d\n", istgt->ErrorRecoveryLevel); timeout = istgt_get_intval(sp, "Timeout"); if (timeout < 0) { timeout = DEFAULT_TIMEOUT; } istgt->timeout = timeout; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "Timeout %d\n", istgt->timeout); nopininterval = istgt_get_intval(sp, "NopInInterval"); if (nopininterval < 0) { nopininterval = DEFAULT_NOPININTERVAL; } istgt->nopininterval = nopininterval; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "NopInInterval %d\n", istgt->nopininterval); maxr2t = istgt_get_intval(sp, "MaxR2T"); if (maxr2t < 0) { maxr2t = DEFAULT_MAXR2T; } if (maxr2t > MAX_R2T) { ISTGT_ERRLOG("MaxR2T(%d) > %d\n", maxr2t, MAX_R2T); return -1; } istgt->maxr2t = maxr2t; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "MaxR2T %d\n", istgt->maxr2t); val = istgt_get_val(sp, "DiscoveryAuthMethod"); if (val == NULL) { istgt->no_discovery_auth = 0; istgt->req_discovery_auth = 0; istgt->req_discovery_auth_mutual = 0; } else { istgt->no_discovery_auth = 0; for (i = 0; ; i++) { val = istgt_get_nmval(sp, "DiscoveryAuthMethod", 0, i); if (val == NULL) break; if (strcasecmp(val, "CHAP") == 0) { istgt->req_discovery_auth = 1; } else if (strcasecmp(val, "Mutual") == 0) { istgt->req_discovery_auth_mutual = 1; } else if (strcasecmp(val, "Auto") == 0) { istgt->req_discovery_auth = 0; istgt->req_discovery_auth_mutual = 0; } else if (strcasecmp(val, "None") == 0) { istgt->no_discovery_auth = 1; istgt->req_discovery_auth = 0; istgt->req_discovery_auth_mutual = 0; } else { ISTGT_ERRLOG("unknown auth\n"); return -1; } } if (istgt->req_discovery_auth_mutual && !istgt->req_discovery_auth) { ISTGT_ERRLOG("Mutual but not CHAP\n"); return -1; } } if (istgt->no_discovery_auth != 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthMethod None\n"); } else if (istgt->req_discovery_auth == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthMethod Auto\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthMethod %s %s\n", istgt->req_discovery_auth ? "CHAP" : "", istgt->req_discovery_auth_mutual ? "Mutual" : ""); } val = istgt_get_val(sp, "DiscoveryAuthGroup"); if (val == NULL) { istgt->discovery_auth_group = 0; } else { ag_tag = val; if (strcasecmp(ag_tag, "None") == 0) { ag_tag_i = 0; } else { if (strncasecmp(ag_tag, "AuthGroup", strlen("AuthGroup")) != 0 || sscanf(ag_tag, "%*[^0-9]%d", &ag_tag_i) != 1) { ISTGT_ERRLOG("auth group error\n"); return -1; } if (ag_tag_i == 0) { ISTGT_ERRLOG("invalid auth group %d\n", ag_tag_i); return -1; } } istgt->discovery_auth_group = ag_tag_i; } if (istgt->discovery_auth_group == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthGroup None\n"); } else { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "DiscoveryAuthGroup AuthGroup%d\n", istgt->discovery_auth_group); } rc = istgt_init_uctl(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_init_uctl() failed\n"); return -1; } rc = istgt_build_uctl_portal(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_build_uctl_portal() failed\n"); return -1; } rc = istgt_build_portal_array(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_build_portal_array() failed\n"); return -1; } rc = istgt_build_initiator_group_array(istgt); if (rc < 0) { ISTGT_ERRLOG("build_initiator_group_array() failed\n"); return -1; } rc = pthread_attr_init(&istgt->attr); if (rc != 0) { ISTGT_ERRLOG("pthread_attr_init() failed\n"); return -1; } rc = pthread_attr_getstacksize(&istgt->attr, &stacksize); if (rc != 0) { ISTGT_ERRLOG("pthread_attr_getstacksize() failed\n"); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "current thread stack = %zd\n", stacksize); if (stacksize < ISTGT_STACKSIZE) { stacksize = ISTGT_STACKSIZE; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "new thread stack = %zd\n", stacksize); rc = pthread_attr_setstacksize(&istgt->attr, stacksize); if (rc != 0) { ISTGT_ERRLOG("pthread_attr_setstacksize() failed\n"); return -1; } } rc = pthread_mutex_init(&istgt->mutex, NULL); if (rc != 0) { ISTGT_ERRLOG("mutex_init() failed\n"); return -1; } /* XXX TODO: add initializer */ istgt_set_state(istgt, ISTGT_STATE_INITIALIZED); return 0; } #ifdef SIGINFO static void istgt_siginfo(int signo) { /* nothing */ } #endif static void istgt_sigwakeup(int signo) { } #ifdef SIGIO static void istgt_sigio(int signo) { } #endif static void * istgt_sighandler(void *arg) { ISTGT_Ptr istgt = (ISTGT_Ptr) arg; sigset_t signew; int signo; sigemptyset(&signew); sigaddset(&signew, SIGINT); sigaddset(&signew, SIGTERM); sigaddset(&signew, SIGQUIT); sigaddset(&signew, SIGHUP); #ifdef SIGINFO sigaddset(&signew, SIGINFO); #endif sigaddset(&signew, SIGUSR1); sigaddset(&signew, SIGUSR2); #ifdef SIGIO sigaddset(&signew, SIGIO); #endif ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop start\n"); while (1) { if (istgt_get_state(istgt) == ISTGT_STATE_EXITING || istgt_get_state(istgt) == ISTGT_STATE_SHUTDOWN) { break; } sigwait(&signew, &signo); switch (signo) { case SIGINT: printf("SIGINT catch\n"); istgt_set_state(istgt, ISTGT_STATE_EXITING); istgt_lu_set_all_state(istgt, ISTGT_STATE_EXITING); break; case SIGTERM: printf("SIGTERM catch\n"); istgt_set_state(istgt, ISTGT_STATE_EXITING); istgt_lu_set_all_state(istgt, ISTGT_STATE_EXITING); break; case SIGQUIT: printf("SIGQUIT catch\n"); exit(EXIT_SUCCESS); break; case SIGHUP: printf("SIGHUP catch\n"); break; #ifdef SIGINFO case SIGINFO: printf("SIGINFO catch\n"); istgt_set_trace_flag(ISTGT_TRACE_ISCSI); break; #endif case SIGUSR1: printf("SIGUSR1 catch\n"); istgt_set_trace_flag(ISTGT_TRACE_NONE); break; case SIGUSR2: printf("SIGUSR2 catch\n"); //istgt_set_trace_flag(ISTGT_TRACE_SCSI); istgt_set_trace_flag(ISTGT_TRACE_ALL); break; #ifdef SIGIO case SIGIO: //printf("SIGIO catch\n"); break; #endif default: break; } } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop ended\n"); return NULL; } static int istgt_acceptor(ISTGT_Ptr istgt) { #ifdef ISTGT_USE_KQUEUE int kq; struct kevent kev; struct timespec kev_timeout; #else struct pollfd fds[MAX_PORTAL + MAX_UCPORTAL]; #endif /* ISTGT_USE_KQUEUE */ struct sockaddr_storage sa; socklen_t salen; int sock; int rc, n; int ucidx; int nidx; int i; if (istgt_get_state(istgt) != ISTGT_STATE_INITIALIZED) { ISTGT_ERRLOG("not initialized\n"); return -1; } /* now running main thread */ istgt_set_state(istgt, ISTGT_STATE_RUNNING); #ifdef ISTGT_USE_KQUEUE kq = kqueue(); if (kq == -1) { ISTGT_ERRLOG("kqueue() failed\n"); return -1; } for (i = 0; i < istgt->nportal; i++) { EV_SET(&kev, istgt->portal[i].sock, EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } } ucidx = istgt->nportal; for (i = 0; i < istgt->nuctl_portal; i++) { EV_SET(&kev, istgt->uctl_portal[i].sock, EVFILT_READ, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } } nidx = istgt->nportal + istgt->nuctl_portal; EV_SET(&kev, SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } EV_SET(&kev, SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL); rc = kevent(kq, &kev, 1, NULL, 0, NULL); if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); close(kq); return -1; } #else memset(&fds, 0, sizeof fds); for (i = 0; i < istgt->nportal; i++) { fds[i].fd = istgt->portal[i].sock; fds[i].events = POLLIN; } ucidx = istgt->nportal; for (i = 0; i < istgt->nuctl_portal; i++) { fds[ucidx + i].fd = istgt->uctl_portal[i].sock; fds[ucidx + i].events = POLLIN; } nidx = istgt->nportal + istgt->nuctl_portal; #endif /* ISTGT_USE_KQUEUE */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop start\n"); while (1) { if (istgt_get_state(istgt) != ISTGT_STATE_RUNNING) { break; } #ifdef ISTGT_USE_KQUEUE //ISTGT_TRACELOG(ISTGT_TRACE_NET, "kevent %d\n", nidx); kev_timeout.tv_sec = 10; kev_timeout.tv_nsec = 0; rc = kevent(kq, NULL, 0, &kev, 1, &kev_timeout); if (rc == -1 && errno == EINTR) { continue; } if (rc == -1) { ISTGT_ERRLOG("kevent() failed\n"); break; } if (rc == 0) { /* idle timeout */ continue; } if (kev.filter == EVFILT_SIGNAL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent SIGNAL\n"); if (kev.ident == SIGINT || kev.ident == SIGTERM) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "kevent SIGNAL SIGINT/SIGTERM\n"); break; } continue; } #else //ISTGT_TRACELOG(ISTGT_TRACE_NET, "poll %d\n", nidx); rc = poll(fds, nidx, POLLWAIT); if (rc == -1 && errno == EINTR) { continue; } if (rc == -1) { ISTGT_ERRLOG("poll() failed\n"); break; } if (rc == 0) { /* no fds */ continue; } #endif /* ISTGT_USE_KQUEUE */ n = rc; for (i = 0; n != 0 && i < istgt->nportal; i++) { #ifdef ISTGT_USE_KQUEUE if (kev.ident == istgt->portal[i].sock) { if (kev.flags) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "flags %x\n", kev.flags); } #else if (fds[i].revents) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "events %x\n", fds[i].revents); } if (fds[i].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ n--; memset(&sa, 0, sizeof(sa)); salen = sizeof(sa); #ifdef ISTGT_USE_KQUEUE ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %d\n", kev.ident); rc = accept(kev.ident, (struct sockaddr *) &sa, &salen); #else ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %d\n", fds[i].fd); rc = accept(fds[i].fd, (struct sockaddr *) &sa, &salen); #endif /* ISTGT_USE_KQUEUE */ if (rc < 0) { ISTGT_ERRLOG("accept error: %d\n", rc); continue; } sock = rc; #if 0 rc = fcntl(sock, F_GETFL, 0); if (rc == -1) { ISTGT_ERRLOG("fcntl() failed\n"); continue; } rc = fcntl(sock, F_SETFL, (rc | O_NONBLOCK)); if (rc == -1) { ISTGT_ERRLOG("fcntl() failed\n"); continue; } #endif rc = istgt_create_conn(istgt, &istgt->portal[i], sock, (struct sockaddr *) &sa, salen); if (rc < 0) { close(sock); ISTGT_ERRLOG("istgt_create_conn() failed\n"); continue; } } } /* check for control */ for (i = 0; n != 0 && i < istgt->nuctl_portal; i++) { #ifdef ISTGT_USE_KQUEUE if (kev.ident == istgt->uctl_portal[i].sock) { if (kev.flags) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "flags %x\n", kev.flags); } #else if (fds[ucidx + i].revents) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "events %x\n", fds[ucidx + i].revents); } if (fds[ucidx + i].revents & POLLIN) { #endif /* ISTGT_USE_KQUEUE */ n--; memset(&sa, 0, sizeof(sa)); salen = sizeof(sa); #ifdef ISTGT_USE_KQUEUE ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %d\n", kev.ident); rc = accept(kev.ident, (struct sockaddr *) &sa, &salen); #else ISTGT_TRACELOG(ISTGT_TRACE_NET, "accept %d\n", fds[ucidx + i].fd); rc = accept(fds[ucidx + i].fd, (struct sockaddr *) &sa, &salen); #endif /* ISTGT_USE_KQUEUE */ if (rc < 0) { ISTGT_ERRLOG("accept error: %d\n", rc); continue; } sock = rc; rc = istgt_create_uctl(istgt, &istgt->uctl_portal[i], sock, (struct sockaddr *) &sa, salen); if (rc < 0) { close(sock); ISTGT_ERRLOG("istgt_create_uctl() failed\n"); continue; } } } } #ifdef ISTGT_USE_KQUEUE close(kq); #endif /* ISTGT_USE_KQUEUE */ ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "loop ended\n"); istgt_set_state(istgt, ISTGT_STATE_EXITING); return 0; } static void usage(void) { printf("istgt [options]\n"); printf("options:\n"); printf(" -c config config file (default %s)\n", DEFAULT_CONFIG); printf(" -p pidfile use specific file\n"); printf(" -l facility use specific syslog facility (default %s)\n", DEFAULT_LOG_FACILITY); printf(" -m mode operational mode (default %d, 0=traditional, " "1=normal, 2=experimental)\n", DEFAULT_ISTGT_SWMODE); printf(" -t flag trace flag (all, net, iscsi, scsi, lu)\n"); printf(" -q quiet warnings\n"); printf(" -D don't detach from tty\n"); printf(" -H show this usage\n"); printf(" -V show version\n"); } int main(int argc, char **argv) { ISTGT_Ptr istgt; const char *config_file = DEFAULT_CONFIG; const char *pidfile = NULL; const char *logfacility = NULL; const char *logpriority = NULL; CONFIG *config; pthread_t sigthread; struct sigaction sigact, sigoldact_pipe, sigoldact_info; struct sigaction sigoldact_wakeup, sigoldact_io; sigset_t signew, sigold; int retry = 10; int detach = 1; int swmode; int ch; int rc; if (sizeof (ISCSI_BHS) != ISCSI_BHS_LEN) { fprintf(stderr, "Internal Error\n"); exit(EXIT_FAILURE); } memset(&g_istgt, 0, sizeof g_istgt); istgt = &g_istgt; istgt_set_state(istgt, ISTGT_STATE_INVALID); istgt->swmode = DEFAULT_ISTGT_SWMODE; while ((ch = getopt(argc, argv, "c:p:l:m:t:qDHV")) != -1) { switch (ch) { case 'c': config_file = optarg; break; case 'p': pidfile = optarg; break; case 'l': logfacility = optarg; break; case 'm': swmode = strtol(optarg, NULL, 10); if (swmode == ISTGT_SWMODE_TRADITIONAL || swmode == ISTGT_SWMODE_NORMAL || swmode == ISTGT_SWMODE_EXPERIMENTAL) { istgt->swmode = swmode; } else { fprintf(stderr, "unknown mode %x\n", swmode); usage(); exit(EXIT_FAILURE); } break; case 't': if (strcasecmp(optarg, "NET") == 0) { istgt_set_trace_flag(ISTGT_TRACE_NET); } else if (strcasecmp(optarg, "ISCSI") == 0) { istgt_set_trace_flag(ISTGT_TRACE_ISCSI); } else if (strcasecmp(optarg, "SCSI") == 0) { istgt_set_trace_flag(ISTGT_TRACE_SCSI); } else if (strcasecmp(optarg, "LU") == 0) { istgt_set_trace_flag(ISTGT_TRACE_LU); } else if (strcasecmp(optarg, "ALL") == 0) { istgt_set_trace_flag(ISTGT_TRACE_ALL); } else if (strcasecmp(optarg, "NONE") == 0) { istgt_set_trace_flag(ISTGT_TRACE_NONE); } else { fprintf(stderr, "unknown flag\n"); usage(); exit(EXIT_FAILURE); } break; case 'q': g_warn_flag = 0; break; case 'D': detach = 0; break; case 'V': printf("istgt version %s\n", ISTGT_VERSION); printf("istgt extra version %s\n", ISTGT_EXTRA_VERSION); exit(EXIT_SUCCESS); case 'H': default: usage(); exit(EXIT_SUCCESS); } } /* read config files */ config = istgt_allocate_config(); rc = istgt_read_config(config, config_file); if (rc < 0) { fprintf(stderr, "config error\n"); exit(EXIT_FAILURE); } if (config->section == NULL) { fprintf(stderr, "empty config\n"); istgt_free_config(config); exit(EXIT_FAILURE); } istgt->config = config; //istgt_print_config(config); /* open log files */ if (logfacility == NULL) { logfacility = istgt_get_log_facility(config); } rc = istgt_set_log_facility(logfacility); if (rc < 0) { fprintf(stderr, "log facility error\n"); istgt_free_config(config); exit(EXIT_FAILURE); } if (logpriority == NULL) { logpriority = DEFAULT_LOG_PRIORITY; } rc = istgt_set_log_priority(logpriority); if (rc < 0) { fprintf(stderr, "log priority error\n"); istgt_free_config(config); exit(EXIT_FAILURE); } istgt_open_log(); ISTGT_NOTICELOG("istgt version %s (%s)\n", ISTGT_VERSION, ISTGT_EXTRA_VERSION); switch (istgt->swmode) { case ISTGT_SWMODE_TRADITIONAL: ISTGT_NOTICELOG("traditional mode\n"); break; case ISTGT_SWMODE_NORMAL: ISTGT_NOTICELOG("normal mode\n"); break; case ISTGT_SWMODE_EXPERIMENTAL: ISTGT_NOTICELOG("experimental mode\n"); break; default: break; } #ifdef ISTGT_USE_CRC32C_TABLE /* build crc32c table */ istgt_init_crc32c_table(); #endif /* ISTGT_USE_CRC32C_TABLE */ /* initialize sub modules */ rc = istgt_init(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_init() failed\n"); initialize_error: istgt_close_log(); istgt_free_config(config); exit(EXIT_FAILURE); } rc = istgt_lu_init(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_lu_init() failed\n"); goto initialize_error; } rc = istgt_iscsi_init(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_iscsi_init() failed\n"); goto initialize_error; } /* override by command line */ if (pidfile != NULL) { xfree(istgt->pidfile); istgt->pidfile = xstrdup(pidfile); } /* detach from tty and run background */ fflush(stdout); if (detach) { rc = daemon(0, 0); if (rc < 0) { ISTGT_ERRLOG("daemon() failed\n"); goto initialize_error; } } /* setup signal handler thread */ memset(&sigact, 0, sizeof sigact); memset(&sigoldact_pipe, 0, sizeof sigoldact_pipe); memset(&sigoldact_info, 0, sizeof sigoldact_info); memset(&sigoldact_wakeup, 0, sizeof sigoldact_wakeup); memset(&sigoldact_io, 0, sizeof sigoldact_io); sigact.sa_handler = SIG_IGN; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGPIPE, &sigact, &sigoldact_pipe); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGPIPE) failed\n"); goto initialize_error; } #ifdef SIGINFO sigact.sa_handler = istgt_siginfo; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGINFO, &sigact, &sigoldact_info); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGINFO) failed\n"); goto initialize_error; } #endif #ifdef ISTGT_USE_SIGRT if (ISTGT_SIGWAKEUP < SIGRTMIN || ISTGT_SIGWAKEUP > SIGRTMAX) { ISTGT_ERRLOG("SIGRT error\n"); goto initialize_error; } #endif /* ISTGT_USE_SIGRT */ sigact.sa_handler = istgt_sigwakeup; sigemptyset(&sigact.sa_mask); rc = sigaction(ISTGT_SIGWAKEUP, &sigact, &sigoldact_wakeup); if (rc < 0) { ISTGT_ERRLOG("sigaction(ISTGT_SIGWAKEUP) failed\n"); goto initialize_error; } #ifdef SIGIO sigact.sa_handler = istgt_sigio; sigemptyset(&sigact.sa_mask); rc = sigaction(SIGIO, &sigact, &sigoldact_io); if (rc < 0) { ISTGT_ERRLOG("sigaction(SIGIO) failed\n"); goto initialize_error; } #endif pthread_sigmask(SIG_SETMASK, NULL, &signew); sigaddset(&signew, SIGINT); sigaddset(&signew, SIGTERM); sigaddset(&signew, SIGQUIT); sigaddset(&signew, SIGHUP); #ifdef SIGINFO sigaddset(&signew, SIGINFO); #endif sigaddset(&signew, SIGUSR1); sigaddset(&signew, SIGUSR2); #ifdef SIGIO sigaddset(&signew, SIGIO); #endif sigaddset(&signew, ISTGT_SIGWAKEUP); pthread_sigmask(SIG_SETMASK, &signew, &sigold); #ifdef ISTGT_STACKSIZE rc = pthread_create(&sigthread, &istgt->attr, &istgt_sighandler, #else rc = pthread_create(&sigthread, NULL, &istgt_sighandler, #endif (void *) istgt); if (rc != 0) { ISTGT_ERRLOG("pthread_create() failed\n"); goto initialize_error; } #if 0 rc = pthread_detach(sigthread); if (rc != 0) { ISTGT_ERRLOG("pthread_detach() failed\n"); goto initialize_error; } #endif #ifdef HAVE_PTHREAD_SET_NAME_NP pthread_set_name_np(sigthread, "sigthread"); pthread_set_name_np(pthread_self(), "mainthread"); #endif /* create LUN threads for command queuing */ rc = istgt_lu_create_threads(istgt); if (rc < 0) { ISTGT_ERRLOG("lu_create_threads() failed\n"); goto initialize_error; } rc = istgt_lu_set_all_state(istgt, ISTGT_STATE_RUNNING); if (rc < 0) { ISTGT_ERRLOG("lu_set_all_state() failed\n"); goto initialize_error; } /* open portals */ rc = istgt_open_uctl_portal(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_open_uctl_portal() failed\n"); goto initialize_error; } rc = istgt_open_portal(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_open_portal() failed\n"); goto initialize_error; } /* write pid */ rc = istgt_write_pidfile(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_write_pid() failed\n"); goto initialize_error; } /* accept loop */ rc = istgt_acceptor(istgt); if (rc < 0) { ISTGT_ERRLOG("istgt_acceptor() failed\n"); istgt_close_portal(istgt); istgt_close_uctl_portal(istgt); istgt_iscsi_shutdown(istgt); istgt_lu_shutdown(istgt); istgt_destory_initiator_group_array(istgt); istgt_destroy_portal_array(istgt); istgt_destroy_uctl_portal(istgt); istgt_remove_pidfile(istgt); istgt_close_log(); istgt->config = NULL; istgt_free_config(config); exit(EXIT_FAILURE); } /* wait threads */ while (retry > 0) { if (istgt_get_active_conns() == 0) { break; } sleep(1); retry--; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "retry=%d\n", retry); ISTGT_NOTICELOG("istgt version %s (%s) exiting\n", ISTGT_VERSION, ISTGT_EXTRA_VERSION); /* cleanup */ istgt_close_portal(istgt); istgt_close_uctl_portal(istgt); istgt_iscsi_shutdown(istgt); istgt_lu_shutdown(istgt); istgt_destory_initiator_group_array(istgt); istgt_destroy_portal_array(istgt); istgt_destroy_uctl_portal(istgt); istgt_remove_pidfile(istgt); istgt_close_log(); istgt->config = NULL; istgt_free_config(config); istgt_set_state(istgt, ISTGT_STATE_SHUTDOWN); /* stop signal thread */ rc = pthread_join(sigthread, NULL); if (rc != 0) { ISTGT_ERRLOG("pthread_join() failed\n"); exit (EXIT_FAILURE); } return 0; } istgt-20111008/src/config.h.in000644 000000 000000 00000020051 11640373417 016017 0ustar00rootwheel000000 000000 /* src/config.h.in. Generated from configure.in by autoheader. */ /* Define if enable debug */ #undef DEBUG /* syslog facility */ #undef DEFAULT_LOG_FACILITY /* syslog priority */ #undef DEFAULT_LOG_PRIORITY /* Define to 1 if you have the header file. */ #undef HAVE_AIO_H /* Define to 1 if you have the `arc4random' function. */ #undef HAVE_ARC4RANDOM /* Define to 1 if you have the header file. */ #undef HAVE_ARPA_INET_H /* Define 1 if you have the function. */ #undef HAVE_ATOMIC_LOAD_ACQ_INT /* Define 1 if you have the function. */ #undef HAVE_ATOMIC_OR_UINT_NV /* Define 1 if you have the function. */ #undef HAVE_ATOMIC_STORE_REL_INT /* Define 1 if you have the function. */ #undef HAVE_ATOMIC_SWAP_UINT /* Define to 1 if you have the header file. */ #undef HAVE_FCNTL_H /* Define to 1 if you have the `ftruncate' function. */ #undef HAVE_FTRUNCATE /* Define to 1 if you have the header file. */ #undef HAVE_INTTYPES_H /* Define to 1 if you have the `cam' library (-lcam). */ #undef HAVE_LIBCAM /* Define to 1 if you have the `crypto' library (-lcrypto). */ #undef HAVE_LIBCRYPTO /* Define to 1 if you have the `pthread' library (-lpthread). */ #undef HAVE_LIBPTHREAD /* Define to 1 if you have the header file. */ #undef HAVE_LIMITS_H /* Define to 1 if you have the header file. */ #undef HAVE_MACHINE_ATOMIC_H /* Define to 1 if your system has a GNU libc compatible `malloc' function, and to 0 otherwise. */ #undef HAVE_MALLOC /* Define 1 if you have the function. */ #undef HAVE_MEMBAR_PRODUCER /* Define to 1 if you have the header file. */ #undef HAVE_MEMORY_H /* Define to 1 if you have the `memset' function. */ #undef HAVE_MEMSET /* Define to 1 if you have the header file. */ #undef HAVE_NETDB_H /* Define to 1 if you have the header file. */ #undef HAVE_NETINET_IN_H /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_H /* Define to 1 if you have the header file. */ #undef HAVE_PTHREAD_NP_H /* Define to 1 if you have the `pthread_set_name_np' function. */ #undef HAVE_PTHREAD_SET_NAME_NP /* Define to 1 if you have the `pthread_yield' function. */ #undef HAVE_PTHREAD_YIELD /* Define to 1 if your system has a GNU libc compatible `realloc' function, and to 0 otherwise. */ #undef HAVE_REALLOC /* Define to 1 if you have the `realpath' function. */ #undef HAVE_REALPATH /* Define to 1 if you have the header file. */ #undef HAVE_SCHED_H /* Define to 1 if you have the `sched_yield' function. */ #undef HAVE_SCHED_YIELD /* Define to 1 if you have the `setproctitle' function. */ #undef HAVE_SETPROCTITLE /* Define to 1 if you have the `socket' function. */ #undef HAVE_SOCKET /* Define to 1 if you have the `srandomdev' function. */ #undef HAVE_SRANDOMDEV /* Define to 1 if you have the header file. */ #undef HAVE_STDDEF_H /* Define to 1 if you have the header file. */ #undef HAVE_STDINT_H /* Define to 1 if you have the header file. */ #undef HAVE_STDLIB_H /* Define to 1 if you have the `strcasecmp' function. */ #undef HAVE_STRCASECMP /* Define to 1 if you have the `strchr' function. */ #undef HAVE_STRCHR /* Define to 1 if you have the header file. */ #undef HAVE_STRINGS_H /* Define to 1 if you have the header file. */ #undef HAVE_STRING_H /* Define to 1 if you have the `strlcpy' function. */ #undef HAVE_STRLCPY /* Define to 1 if you have the `strncasecmp' function. */ #undef HAVE_STRNCASECMP /* Define to 1 if you have the `strspn' function. */ #undef HAVE_STRSPN /* Define to 1 if you have the `strtol' function. */ #undef HAVE_STRTOL /* Define to 1 if you have the `strtoull' function. */ #undef HAVE_STRTOULL /* Define to 1 if `st_blocks' is a member of `struct stat'. */ #undef HAVE_STRUCT_STAT_ST_BLOCKS /* Define to 1 if your `struct stat' has `st_blocks'. Deprecated, use `HAVE_STRUCT_STAT_ST_BLOCKS' instead. */ #undef HAVE_ST_BLOCKS /* Define to 1 if you have the header file. */ #undef HAVE_SYSLOG_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_ATOMIC_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_DISKLABEL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_DISK_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_IOCTL_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_PARAM_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_SOCKET_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_STAT_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TIME_H /* Define to 1 if you have the header file. */ #undef HAVE_SYS_TYPES_H /* Define to 1 if you have the header file. */ #undef HAVE_UNISTD_H /* Define to 1 if you have the header file. */ #undef HAVE_UUID_H /* Define to 1 if `lstat' dereferences a symlink specified with a trailing slash. */ #undef LSTAT_FOLLOWS_SLASHED_SYMLINK /* Define to the address where bug reports for this package should be sent. */ #undef PACKAGE_BUGREPORT /* Define to the full name of this package. */ #undef PACKAGE_NAME /* Define to the full name and version of this package. */ #undef PACKAGE_STRING /* Define to the one symbol short name of this package. */ #undef PACKAGE_TARNAME /* Define to the home page for this package. */ #undef PACKAGE_URL /* Define to the version of this package. */ #undef PACKAGE_VERSION /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS /* Use atomic */ #undef USE_ATOMIC /* Number of bits in a file offset, on hosts where this is settable. */ #undef _FILE_OFFSET_BITS /* Define for large files, on AIX-style hosts. */ #undef _LARGE_FILES /* Define for Solaris 2.5.1 so the uint32_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT32_T /* Define for Solaris 2.5.1 so the uint64_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT64_T /* Define for Solaris 2.5.1 so the uint8_t typedef from , , or is not used. If the typedef were allowed, the #define below would cause a syntax error. */ #undef _UINT8_T /* Define to `__inline__' or `__inline' if that's what the C compiler calls it, or to nothing if 'inline' is not supported under any name. */ #ifndef __cplusplus #undef inline #endif /* Define to the type of a signed integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef int32_t /* Define to the type of a signed integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef int64_t /* Define to rpl_malloc if the replacement function should be used. */ #undef malloc /* Define to `long int' if does not define. */ #undef off_t /* Define to `int' if does not define. */ #undef pid_t /* Define to rpl_realloc if the replacement function should be used. */ #undef realloc /* Define to `unsigned int' if does not define. */ #undef size_t /* Define to `int' if does not define. */ #undef ssize_t /* Define to the type of an unsigned integer type of width exactly 16 bits if such a type exists and the standard includes do not define it. */ #undef uint16_t /* Define to the type of an unsigned integer type of width exactly 32 bits if such a type exists and the standard includes do not define it. */ #undef uint32_t /* Define to the type of an unsigned integer type of width exactly 64 bits if such a type exists and the standard includes do not define it. */ #undef uint64_t /* Define to the type of an unsigned integer type of width exactly 8 bits if such a type exists and the standard includes do not define it. */ #undef uint8_t istgt-20111008/src/istgt_iscsi.h000644 000000 000000 00000017642 11635327750 016510 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_ISCSI_H #define ISTGT_ISCSI_H #include #include #include "istgt.h" #include "istgt_iscsi_param.h" #include "istgt_lu.h" #include "istgt_queue.h" #define ISCSI_BHS_LEN 48 #define ISCSI_DIGEST_LEN 4 #define ISCSI_ALIGNMENT 4 /* support version - RFC3720(10.12.4) */ #define ISCSI_VERSION 0x00 #define ISCSI_ALIGN(SIZE) \ (((SIZE) + (ISCSI_ALIGNMENT - 1)) & ~(ISCSI_ALIGNMENT - 1)) #define ISCSI_TEXT_MAX_KEY_LEN 64 /* for authentication key (non encoded 1024bytes) RFC3720(5.1/11.1.4) */ #define ISCSI_TEXT_MAX_VAL_LEN 8192 #define SESS_MTX_LOCK(CONN) \ do { \ if (pthread_mutex_lock(&(CONN)->sess->mutex) != 0) { \ ISTGT_ERRLOG("sess lock error\n"); \ pthread_exit(NULL); \ } \ } while (0) #define SESS_MTX_UNLOCK(CONN) \ do { \ if (pthread_mutex_unlock(&(CONN)->sess->mutex) != 0) { \ ISTGT_ERRLOG("sess unlock error\n"); \ pthread_exit(NULL); \ } \ } while (0) typedef enum { /* Initiator opcodes */ ISCSI_OP_NOPOUT = 0x00, ISCSI_OP_SCSI = 0x01, ISCSI_OP_TASK = 0x02, ISCSI_OP_LOGIN = 0x03, ISCSI_OP_TEXT = 0x04, ISCSI_OP_SCSI_DATAOUT = 0x05, ISCSI_OP_LOGOUT = 0x06, ISCSI_OP_SNACK = 0x10, ISCSI_OP_VENDOR_1C = 0x1c, ISCSI_OP_VENDOR_1D = 0x1d, ISCSI_OP_VENDOR_1E = 0x1e, /* Target opcodes */ ISCSI_OP_NOPIN = 0x20, ISCSI_OP_SCSI_RSP = 0x21, ISCSI_OP_TASK_RSP = 0x22, ISCSI_OP_LOGIN_RSP = 0x23, ISCSI_OP_TEXT_RSP = 0x24, ISCSI_OP_SCSI_DATAIN = 0x25, ISCSI_OP_LOGOUT_RSP = 0x26, ISCSI_OP_R2T = 0x31, ISCSI_OP_ASYNC = 0x32, ISCSI_OP_VENDOR_3C = 0x3c, ISCSI_OP_VENDOR_3D = 0x3d, ISCSI_OP_VENDOR_3E = 0x3e, ISCSI_OP_REJECT = 0x3f, } ISCSI_OP; typedef enum { ISCSI_TASK_FUNC_ABORT_TASK = 1, ISCSI_TASK_FUNC_ABORT_TASK_SET = 2, ISCSI_TASK_FUNC_CLEAR_ACA = 3, ISCSI_TASK_FUNC_CLEAR_TASK_SET = 4, ISCSI_TASK_FUNC_LOGICAL_UNIT_RESET = 5, ISCSI_TASK_FUNC_TARGET_WARM_RESET = 6, ISCSI_TASK_FUNC_TARGET_COLD_RESET = 7, ISCSI_TASK_FUNC_TASK_REASSIGN = 8, } ISCSI_TASK_FUNC; typedef struct iscsi_bhs_t { /* 0-3 */ uint8_t opcode; uint8_t opcode_specific1[3]; /* 4-7 */ uint8_t total_ahs_len; uint8_t data_segment_len[3]; /* 8-11 */ union { uint8_t lun1[4]; uint8_t opcode_specific2[4]; } u1; /* 12-15 */ union { uint8_t lun2[4]; uint8_t opcode_specific3[4]; } u2; /* 16-19 */ uint8_t inititator_task_tag[4]; /* 20-47 */ uint8_t opcode_specific4[28]; } ISCSI_BHS; typedef struct iscsi_ahs_t { /* 0-3 */ uint8_t ahs_len[2]; uint8_t ahs_type; uint8_t ahs_specific1; /* 4-x */ uint8_t ahs_specific2[]; } ISCSI_AHS; typedef struct iscsi_pdu_t { ISCSI_BHS bhs; ISCSI_AHS *ahs; uint8_t header_digest[ISCSI_DIGEST_LEN]; uint8_t shortdata[ISTGT_SHORTDATASIZE]; uint8_t *data; uint8_t data_digest[ISCSI_DIGEST_LEN]; int total_ahs_len; int data_segment_len; int copy_pdu; } ISCSI_PDU; typedef ISCSI_PDU *ISCSI_PDU_Ptr; typedef enum { CONN_STATE_INVALID = 0, CONN_STATE_RUNNING = 1, CONN_STATE_EXITING = 2, CONN_STATE_SHUTDOWN = 3, } CONN_STATE; typedef enum { ISCSI_LOGIN_PHASE_NONE = 0, ISCSI_LOGIN_PHASE_START = 1, ISCSI_LOGIN_PHASE_SECURITY = 2, ISCSI_LOGIN_PHASE_OPERATIONAL = 3, ISCSI_LOGIN_PHASE_FULLFEATURE = 4, } ISCSI_LOGIN_PHASE; typedef enum { ISTGT_CHAP_PHASE_NONE = 0, ISTGT_CHAP_PHASE_WAIT_A = 1, ISTGT_CHAP_PHASE_WAIT_NR = 2, ISTGT_CHAP_PHASE_END = 3, } ISTGT_CHAP_PHASE; #define ISTGT_CHAP_CHALLENGE_LEN 1024 typedef struct istgt_chap_auth_t { ISTGT_CHAP_PHASE chap_phase; char *user; char *secret; char *muser; char *msecret; uint8_t chap_id[1]; uint8_t chap_mid[1]; int chap_challenge_len; uint8_t chap_challenge[ISTGT_CHAP_CHALLENGE_LEN]; int chap_mchallenge_len; uint8_t chap_mchallenge[ISTGT_CHAP_CHALLENGE_LEN]; } ISTGT_CHAP_AUTH; typedef struct istgt_r2t_task_t { struct istgt_conn_t *conn; ISTGT_LU_Ptr lu; uint64_t lun; uint32_t CmdSN; uint32_t task_tag; uint32_t transfer_len; uint32_t transfer_tag; int iobufsize; uint8_t *iobuf; uint32_t R2TSN; uint32_t DataSN; int F_bit; int offset; } ISTGT_R2T_TASK; typedef ISTGT_R2T_TASK *ISTGT_R2T_TASK_Ptr; typedef struct istgt_conn_t { int id; ISTGT_Ptr istgt; PORTAL portal; int sock; int wsock; #ifdef ISTGT_USE_KQUEUE int kq; #endif /* ISTGT_USE_KQUEUE */ int use_sender; pthread_t thread; pthread_t sender_thread; pthread_mutex_t sender_mutex; pthread_cond_t sender_cond; struct istgt_sess_t *sess; CONN_STATE state; int exec_logout; int max_pending; int queue_depth; pthread_mutex_t wpdu_mutex; pthread_cond_t wpdu_cond; ISTGT_QUEUE pending_pdus; ISCSI_PDU pdu; int max_r2t; int pending_r2t; pthread_mutex_t r2t_mutex; ISTGT_R2T_TASK_Ptr *r2t_tasks; int task_pipe[2]; int max_task_queue; pthread_mutex_t task_queue_mutex; ISTGT_QUEUE task_queue; pthread_mutex_t result_queue_mutex; pthread_cond_t result_queue_cond; ISTGT_QUEUE result_queue; ISTGT_LU_TASK_Ptr exec_lu_task; int running_tasks; uint16_t cid; /* IP address */ int initiator_family; char initiator_addr[MAX_INITIATOR_ADDR]; char target_addr[MAX_TARGET_ADDR]; /* Initiator/Target port binds */ char initiator_name[MAX_INITIATOR_NAME]; char target_name[MAX_TARGET_NAME]; char initiator_port[MAX_INITIATOR_NAME]; char target_port[MAX_TARGET_NAME]; /* for fast access */ int header_digest; int data_digest; int full_feature; ISCSI_PARAM *params; ISCSI_LOGIN_PHASE login_phase; ISTGT_CHAP_AUTH auth; int authenticated; int req_auth; int req_mutual; int timeout; int nopininterval; int TargetMaxRecvDataSegmentLength; int MaxRecvDataSegmentLength; int MaxOutstandingR2T; int FirstBurstLength; int MaxBurstLength; int shortpdusize; uint8_t *shortpdu; int iobufsize; uint8_t *iobuf; int snsbufsize; uint8_t *snsbuf; int recvbufsize; int sendbufsize; uint8_t *recvbuf; uint8_t *sendbuf; int worksize; uint8_t *workbuf; uint32_t StatSN; } CONN; typedef CONN *CONN_Ptr; typedef struct istgt_sess_t { int connections; int max_conns; CONN_Ptr *conns; pthread_mutex_t mutex; pthread_cond_t mcs_cond; int req_mcs_cond; char *initiator_port; char *target_name; int tag; uint64_t isid; uint16_t tsih; ISTGT_LU_Ptr lu; ISCSI_PARAM *params; int MaxConnections; int MaxOutstandingR2T; int DefaultTime2Wait; int DefaultTime2Retain; int FirstBurstLength; int MaxBurstLength; int InitialR2T; int ImmediateData; int DataPDUInOrder; int DataSequenceInOrder; int ErrorRecoveryLevel; int initial_r2t; int immediate_data; uint32_t ExpCmdSN; uint32_t MaxCmdSN; } SESS; typedef SESS *SESS_Ptr; #endif /* ISTGT_ISCSI_H */ istgt-20111008/src/istgt_crc32c.c000644 000000 000000 00000011711 11317704406 016431 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include "istgt_iscsi.h" #include "istgt_crc32c.h" /* defined in RFC3720(12.1) */ static uint32_t istgt_crc32c_initial = ISTGT_CRC32C_INITIAL; static uint32_t istgt_crc32c_xor = ISTGT_CRC32C_XOR; static uint32_t istgt_crc32c_polynomial = ISTGT_CRC32C_POLYNOMIAL; #ifdef ISTGT_USE_CRC32C_TABLE static uint32_t istgt_crc32c_table[256]; static int istgt_crc32c_initialized = 0; #endif /* ISTGT_USE_CRC32C_TABLE */ static uint32_t istgt_reflect(uint32_t val, int bits) { int i; uint32_t r; if (bits < 1 || bits > 32) return 0; r = 0; for (i = 0; i < bits; i++) { r |= ((val >> ((bits - 1) - i)) & 1) << i; } return r; } #ifdef ISTGT_USE_CRC32C_TABLE void istgt_init_crc32c_table(void) { int i, j; uint32_t val; uint32_t reflect_polynomial; reflect_polynomial = istgt_reflect(istgt_crc32c_polynomial, 32); for (i = 0; i < 256; i++) { val = i; for (j = 0; j < 8; j++) { if (val & 1) { val = (val >> 1) ^ reflect_polynomial; } else { val = (val >> 1); } } istgt_crc32c_table[i] = val; } istgt_crc32c_initialized = 1; } #endif /* ISTGT_USE_CRC32C_TABLE */ uint32_t istgt_update_crc32c(const uint8_t *buf, size_t len, uint32_t crc) { size_t s; #ifndef ISTGT_USE_CRC32C_TABLE int i; uint32_t val; uint32_t reflect_polynomial; #endif /* ISTGT_USE_CRC32C_TABLE */ #ifdef ISTGT_USE_CRC32C_TABLE #if 0 /* initialize by main() */ if (!istgt_crc32c_initialized) { istgt_init_crc32c_table(); } #endif #else reflect_polynomial = istgt_reflect(istgt_crc32c_polynomial, 32); #endif /* ISTGT_USE_CRC32C_TABLE */ for (s = 0; s < len; s++) { #ifdef ISTGT_USE_CRC32C_TABLE crc = (crc >> 8) ^ istgt_crc32c_table[(crc ^ buf[s]) & 0xff]; #else val = buf[s]; for (i = 0; i < 8; i++) { if ((crc ^ val) & 1) { crc = (crc >> 1) ^ reflect_polynomial; } else { crc = (crc >> 1); } val = val >> 1; } #endif /* ISTGT_USE_CRC32C_TABLE */ } return crc; } uint32_t istgt_fixup_crc32c(size_t total, uint32_t crc) { uint8_t padding[ISCSI_ALIGNMENT]; size_t pad_length; size_t rest; if (total == 0) return crc; #if 0 /* alignment must be power of 2 */ rest = total & ~(ISCSI_ALIGNMENT - 1); #endif rest = total % ISCSI_ALIGNMENT; if (rest != 0) { pad_length = ISCSI_ALIGNMENT; pad_length -= rest; if (pad_length > 0 && pad_length < sizeof padding){ memset(padding, 0, sizeof padding); crc = istgt_update_crc32c(padding, pad_length, crc); } } return crc; } uint32_t istgt_crc32c(const uint8_t *buf, size_t len) { uint32_t crc32c; crc32c = istgt_crc32c_initial; crc32c = istgt_update_crc32c(buf, len, crc32c); if ((len % ISCSI_ALIGNMENT) != 0) { crc32c = istgt_fixup_crc32c(len, crc32c); } crc32c = crc32c ^ istgt_crc32c_xor; return crc32c; } uint32_t istgt_iovec_crc32c(const struct iovec *iovp, int iovc, uint32_t offset, uint32_t len) { const uint8_t *p; uint32_t total; uint32_t pos; uint32_t n; uint32_t crc32c; int i; pos = 0; total = 0; crc32c = istgt_crc32c_initial; for (i = 0; i < iovc; i++) { if (len == 0) break; if (pos + iovp[i].iov_len > offset) { p = (const uint8_t *) iovp[i].iov_base + (offset - pos); if (iovp[i].iov_len > len) { n = len; len = 0; } else { n = iovp[i].iov_len; len -= n; } crc32c = istgt_update_crc32c(p, n, crc32c); offset += n; total += n; } pos += iovp[i].iov_len; } #if 0 printf("update %d bytes\n", total); #endif crc32c = istgt_fixup_crc32c(total, crc32c); crc32c = crc32c ^ istgt_crc32c_xor; return crc32c; } istgt-20111008/src/istgt_proto.h000644 000000 000000 00000021323 11640373417 016525 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_PROTO_H #define ISTGT_PROTO_H #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef USE_ATOMIC #ifdef HAVE_SYS_TYPES_H #include #endif #ifdef HAVE_MACHINE_ATOMIC_H #include #endif #ifdef HAVE_SYS_ATOMIC_H #include #endif #endif /* USE_ATOMIC */ #include #include #include #ifdef HAVE_UUID_H #include #endif #include "istgt.h" #include "istgt_iscsi.h" #include "istgt_lu.h" /* istgt_iscsi.c */ int istgt_chap_get_authinfo(ISTGT_CHAP_AUTH *auth, const char *authfile, const char *authuser, int ag_tag); int istgt_iscsi_transfer_out(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *data, int alloc_len, int transfer_len); int istgt_create_sess(ISTGT_Ptr istgt, CONN_Ptr conn, ISTGT_LU_Ptr lu); int istgt_create_conn(ISTGT_Ptr istgt, PORTAL_Ptr portal, int sock, struct sockaddr *sa, socklen_t salen); void istgt_lock_gconns(void); void istgt_unlock_gconns(void); int istgt_get_gnconns(void); CONN_Ptr istgt_get_gconn(int idx); int istgt_get_active_conns(void); CONN_Ptr istgt_find_conn(const char *initiator_port, const char *target_name, uint16_t tsih); int istgt_iscsi_init(ISTGT_Ptr istgt); int istgt_iscsi_shutdown(ISTGT_Ptr istgt); int istgt_iscsi_copy_pdu(ISCSI_PDU_Ptr dst_pdu, ISCSI_PDU_Ptr src_pdu); /* istgt_lu.c */ int istgt_lu_allow_netmask(const char *netmask, const char *addr); int istgt_lu_access(CONN_Ptr conn, ISTGT_LU_Ptr lu, const char *iqn, const char *addr); int istgt_lu_visible(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu, const char *iqn, int pg_tag); int istgt_lu_sendtargets(CONN_Ptr conn, const char *iiqn, const char *iaddr, const char *tiqn, uint8_t *data, int alloc_len, int data_len); ISTGT_LU_Ptr istgt_lu_find_target(ISTGT_Ptr istgt, const char *target_name); uint16_t istgt_lu_allocate_tsih(ISTGT_LU_Ptr lu, const char *initiator_port, int tag); void istgt_lu_free_tsih(ISTGT_LU_Ptr lu, uint16_t tsih, char *initiator_port); char *istgt_lu_get_media_flags_string(int flags, char *buf, size_t len); uint64_t istgt_lu_get_devsize(const char *file); uint64_t istgt_lu_get_filesize(const char *file); uint64_t istgt_lu_parse_size(const char *size); int istgt_lu_parse_media_flags(const char *flags); uint64_t istgt_lu_parse_media_size(const char *file, const char *size, int *flags); PORTAL *istgt_lu_find_portalgroup(ISTGT_Ptr istgt, int tag); INITIATOR_GROUP *istgt_lu_find_initiatorgroup(ISTGT_Ptr istgt, int tag); int istgt_lu_init(ISTGT_Ptr istgt); int istgt_lu_set_all_state(ISTGT_Ptr istgt, ISTGT_STATE state); int istgt_lu_create_threads(ISTGT_Ptr istgt); int istgt_lu_shutdown(ISTGT_Ptr istgt); int istgt_lu_islun2lun(uint64_t islun); uint64_t istgt_lu_lun2islun(int lun, int maxlun); int istgt_lu_reset(ISTGT_LU_Ptr lu, uint64_t lun); int istgt_lu_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); int istgt_lu_create_task(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, ISTGT_LU_TASK_Ptr lu_task, int lun); int istgt_lu_destroy_task(ISTGT_LU_TASK_Ptr lu_task); int istgt_lu_clear_task_IT(CONN_Ptr conn, ISTGT_LU_Ptr lu); int istgt_lu_clear_task_ITL(CONN_Ptr conn, ISTGT_LU_Ptr lu, uint64_t lun); int istgt_lu_clear_task_ITLQ(CONN_Ptr conn, ISTGT_LU_Ptr lu, uint64_t lun, uint32_t CmdSN); int istgt_lu_clear_all_task(ISTGT_LU_Ptr lu, uint64_t lun); /* istgt_lu_ctl.c */ int istgt_create_uctl(ISTGT_Ptr istgt, PORTAL_Ptr portal, int sock, struct sockaddr *sa, socklen_t salen); int istgt_init_uctl(ISTGT_Ptr istgt); /* istgt_lu_disk.c */ struct istgt_lu_disk_t; void istgt_scsi_dump_cdb(uint8_t *cdb); void istgt_strcpy_pad(uint8_t *dst, size_t size, const char *src, int pad); #ifdef HAVE_UUID_H uint64_t istgt_uuid2uint64(uuid_t *uuid); #endif /* HAVE_UUID_H */ uint64_t istgt_get_lui(const char *name, int lun); uint64_t istgt_get_rkey(const char *initiator_name, uint64_t lui); int istgt_lu_set_lid(uint8_t *buf, uint64_t vid); int istgt_lu_set_id(uint8_t *buf, uint64_t vid); int istgt_lu_set_extid(uint8_t *buf, uint64_t vid, uint64_t vide); int istgt_lu_scsi_build_sense_data(uint8_t *data, int sk, int asc, int ascq); int istgt_lu_scsi_build_sense_data2(uint8_t *data, int sk, int asc, int ascq); int istgt_lu_disk_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_disk_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_disk_reset(ISTGT_LU_Ptr lu, int lun); int istgt_lu_disk_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); int istgt_lu_disk_queue_clear_IT(CONN_Ptr conn, ISTGT_LU_Ptr lu); int istgt_lu_disk_queue_clear_ITL(CONN_Ptr conn, ISTGT_LU_Ptr lu, int lun); int istgt_lu_disk_queue_clear_ITLQ(CONN_Ptr conn, ISTGT_LU_Ptr lu, int lun, uint32_t CmdSN); int istgt_lu_disk_queue_clear_all(ISTGT_LU_Ptr lu, int lun); int istgt_lu_disk_queue(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); int istgt_lu_disk_queue_count(ISTGT_LU_Ptr lu, int *lun); int istgt_lu_disk_queue_start(ISTGT_LU_Ptr lu, int lun); void istgt_lu_disk_aio_done(siginfo_t *info); /* istgt_lu_dvd.c */ struct istgt_lu_dvd_t; int istgt_lu_dvd_media_present(struct istgt_lu_dvd_t *spec); int istgt_lu_dvd_media_lock(struct istgt_lu_dvd_t *spec); int istgt_lu_dvd_load_media(struct istgt_lu_dvd_t *spec); int istgt_lu_dvd_unload_media(struct istgt_lu_dvd_t *spec); int istgt_lu_dvd_change_media(struct istgt_lu_dvd_t *spec, char *type, char *flags, char *file, char *size); int istgt_lu_dvd_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_dvd_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_dvd_reset(ISTGT_LU_Ptr lu, int lun); int istgt_lu_dvd_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); /* istgt_lu_tape.c */ struct istgt_lu_tape_t; int istgt_lu_tape_media_present(struct istgt_lu_tape_t *spec); int istgt_lu_tape_media_lock(struct istgt_lu_tape_t *spec); int istgt_lu_tape_load_media(struct istgt_lu_tape_t *spec); int istgt_lu_tape_unload_media(struct istgt_lu_tape_t *spec); int istgt_lu_tape_change_media(struct istgt_lu_tape_t *spec, char *type, char *flags, char *file, char *size); int istgt_lu_tape_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_tape_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_tape_reset(ISTGT_LU_Ptr lu, int lun); int istgt_lu_tape_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); /* istgt_lu_pass.c */ int istgt_lu_pass_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_pass_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu); int istgt_lu_pass_reset(ISTGT_LU_Ptr lu, int lun); int istgt_lu_pass_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd); #ifdef USE_ATOMIC static inline int istgt_lu_get_state(ISTGT_LU_Ptr lu) { ISTGT_STATE state; #if defined HAVE_ATOMIC_LOAD_ACQ_INT state = atomic_load_acq_int((unsigned int *)&lu->state); #elif defined HAVE_ATOMIC_OR_UINT_NV state = (int)atomic_or_uint_nv((unsigned int *)&lu->state, 0); #else #error "no atomic operation" #endif return state; } static inline void istgt_lu_set_state(ISTGT_LU_Ptr lu, ISTGT_STATE state) { #if defined HAVE_ATOMIC_STORE_REL_INT atomic_store_rel_int((unsigned int *)&lu->state, state); #elif defined HAVE_ATOMIC_SWAP_UINT (void)atomic_swap_uint((unsigned int *)&lu->state, state); #if defined HAVE_MEMBAR_PRODUCER membar_producer(); #endif #else #error "no atomic operation" #endif } #else static inline int istgt_lu_get_state(ISTGT_LU_Ptr lu) { ISTGT_STATE state; MTX_LOCK(&lu->state_mutex); state = lu->state; MTX_UNLOCK(&lu->state_mutex); return state; } static inline void istgt_lu_set_state(ISTGT_LU_Ptr lu, ISTGT_STATE state) { MTX_LOCK(&lu->state_mutex); lu->state = state; MTX_UNLOCK(&lu->state_mutex); } #endif /* USE_ATOMIC */ #endif /* ISTGT_PROTO_H */ istgt-20111008/src/istgt_sock.h000644 000000 000000 00000004063 11317704406 016320 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_SOCK_H #define ISTGT_SOCK_H #include #include int istgt_getaddr(int sock, char *saddr, int slen, char *caddr, int clen); int istgt_listen(const char *ip, int port); int istgt_connect(const char *host, int port); int istgt_set_recvtimeout(int s, int msec); int istgt_set_sendtimeout(int s, int msec); ssize_t istgt_read_socket(int s, void *buf, size_t nbytes, int timeout); ssize_t istgt_write_socket(int s, const void *buf, size_t nbytes, int timeout); ssize_t istgt_readline_socket(int sock, char *buf, size_t size, char *tmp, size_t tmpsize, int *tmpidx, int *tmpcnt, int timeout); ssize_t istgt_writeline_socket(int sock, const char *buf, int timeout); #endif /* ISTGT_SOCK_H */ istgt-20111008/src/istgt_iscsi_param.c000644 000000 000000 00000013232 11317704406 017644 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include "istgt.h" #include "istgt_log.h" #include "istgt_misc.h" #include "istgt_iscsi.h" #include "istgt_iscsi_param.h" void istgt_iscsi_param_free(ISCSI_PARAM *params) { ISCSI_PARAM *param, *next_param; if (params == NULL) return; for (param = params; param != NULL; param = next_param) { next_param = param->next; xfree(param->list); xfree(param->val); xfree(param->key); xfree(param); } } ISCSI_PARAM * istgt_iscsi_param_find(ISCSI_PARAM *params, const char *key) { ISCSI_PARAM *param; if (params == NULL || key == NULL) return NULL; for (param = params; param != NULL; param = param->next) { if (param->key != NULL && param->key[0] == key[0] && strcasecmp(param->key, key) == 0) { return param; } } return NULL; } int istgt_iscsi_param_del(ISCSI_PARAM **params, const char *key) { ISCSI_PARAM *param, *prev_param = NULL; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "del %s\n", key); if (params == NULL || key == NULL) return 0; for (param = *params; param != NULL; param = param->next) { if (param->key != NULL && param->key[0] == key[0] && strcasecmp(param->key, key) == 0) { if (prev_param != NULL) { prev_param->next = param->next; } else { *params = param->next; } param->next = NULL; istgt_iscsi_param_free(param); return 0; } prev_param = param; } return -1; } int istgt_iscsi_param_add(ISCSI_PARAM **params, const char *key, const char *val, const char *list, int type) { ISCSI_PARAM *param, *last_param; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "add %s=%s, list=[%s], type=%d\n", key, val, list, type); if (key == NULL) return -1; param = istgt_iscsi_param_find(*params, key); if (param != NULL) { istgt_iscsi_param_del(params, key); } param = xmalloc(sizeof *param); memset(param, 0, sizeof *param); param->next = NULL; param->key = xstrdup(key); param->val = xstrdup(val); param->list = xstrdup(list); param->type = type; last_param = *params; if (last_param != NULL) { while (last_param->next != NULL) { last_param = last_param->next; } last_param->next = param; } else { *params = param; } return 0; } int istgt_iscsi_param_set(ISCSI_PARAM *params, const char *key, const char *val) { ISCSI_PARAM *param; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set %s=%s\n", key, val); param = istgt_iscsi_param_find(params, key); if (param == NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "no key %s\n", key); return -1; } xfree(param->val); param->val = xstrdup(val); return 0; } int istgt_iscsi_param_set_int(ISCSI_PARAM *params, const char *key, int val) { char buf[MAX_TMPBUF]; ISCSI_PARAM *param; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "set %s=%d\n", key, val); param = istgt_iscsi_param_find(params, key); if (param == NULL) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "no key %s\n", key); return -1; } xfree(param->val); snprintf(buf, sizeof buf, "%d", val); param->val = xstrdup(buf); return 0; } int istgt_iscsi_parse_params(ISCSI_PARAM **params, const uint8_t *data, int len) { const uint8_t *p, *q; const uint8_t *last; char *key = NULL; char *val = NULL; int rc; int n; /* for each key/val temporary store */ key = xmalloc(ISCSI_TEXT_MAX_KEY_LEN + 1); val = xmalloc(ISCSI_TEXT_MAX_VAL_LEN + 1); /* data = "KEY=VALKEY=VAL..." */ p = data; last = data + len; while (p < last && *p != '\0') { q = p; /* q = "KEY=VAL" */ while (q < last && *q != '\0') { if (*q == '=') { break; } q++; } if (q >= last || *q == '\0') { ISTGT_ERRLOG("'=' not found\n"); error_return: xfree(key); xfree(val); return -1; } n = q - p; if (n > ISCSI_TEXT_MAX_KEY_LEN) { ISTGT_ERRLOG("Overflow Key %d\n", n); goto error_return; } memcpy(key, p, n); key[n] = '\0'; p = q + 1; q = p; /* q = "VAL" */ while (q < last && *q != '\0') { q++; } n = q - p; if (n > ISCSI_TEXT_MAX_VAL_LEN) { ISTGT_ERRLOG("Overflow Val %d\n", n); goto error_return; } memcpy(val, p, n); val[n] = '\0'; rc = istgt_iscsi_param_add(params, key, val, NULL, 0); if (rc < 0) { ISTGT_ERRLOG("iscsi_param_add() failed\n"); goto error_return; } p = q + 1; while (p < last && *p == '\0') { p++; } } xfree(key); xfree(val); return 0; } istgt-20111008/src/istgt_md5.h000644 000000 000000 00000003354 11317704406 016050 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2010 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_MD5_H #define ISTGT_MD5_H #include #include #define ISTGT_MD5DIGEST_LEN MD5_DIGEST_LENGTH typedef struct istgt_md5ctx_t { MD5_CTX md5ctx; } ISTGT_MD5CTX; int istgt_md5init(ISTGT_MD5CTX *md5ctx); int istgt_md5final(void *md5, ISTGT_MD5CTX *md5ctx); int istgt_md5update(ISTGT_MD5CTX *md5ctx, const void *data, size_t len); #endif /* ISTGT_MD5_H */ istgt-20111008/src/istgt_lu.h000644 000000 000000 00000016664 11643630071 016011 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifndef ISTGT_LU_H #define ISTGT_LU_H #include #include #include "istgt.h" #define MAX_LU_LUN 16 #define MAX_LU_LUN_SLOT 8 #define MAX_LU_TSIH 256 #define MAX_LU_MAP 256 #define MAX_LU_SERIAL_STRING 32 #define MAX_LU_RESERVE 256 #define MAX_LU_RESERVE_IPT 256 #define MAX_LU_QUEUE_DEPTH 256 #define USE_LU_TAPE_DLT8000 #define DEFAULT_LU_BLOCKLEN 512 #define DEFAULT_LU_BLOCKLEN_DISK DEFAULT_LU_BLOCKLEN #define DEFAULT_LU_BLOCKLEN_DVD 2048 #define DEFAULT_LU_BLOCKLEN_TAPE DEFAULT_LU_BLOCKLEN #define DEFAULT_LU_QUEUE_DEPTH 32 #define DEFAULT_LU_ROTATIONRATE 7200 /* 7200 rpm */ #define DEFAULT_LU_FORMFACTOR 0x02 /* 3.5 inch */ #if defined (__FreeBSD__) #define DEFAULT_LU_VENDOR "FreeBSD" #elif defined (__NetBSD__) #define DEFAULT_LU_VENDOR "NetBSD" #elif defined (__OpenBSD__) #define DEFAULT_LU_VENDOR "OpenBSD" #else //#define DEFAULT_LU_VENDOR "PEACHNW" #define DEFAULT_LU_VENDOR "FreeBSD" #endif #define DEFAULT_LU_VENDOR_DISK DEFAULT_LU_VENDOR #define DEFAULT_LU_VENDOR_DVD DEFAULT_LU_VENDOR #ifndef USE_LU_TAPE_DLT8000 #define DEFAULT_LU_VENDOR_TAPE DEFAULT_LU_VENDOR #else #define DEFAULT_LU_VENDOR_TAPE "QUANTUM" #endif /* !USE_LU_TAPE_DLT8000 */ #define DEFAULT_LU_PRODUCT "iSCSI UNIT" #define DEFAULT_LU_PRODUCT_DISK "iSCSI DISK" #define DEFAULT_LU_PRODUCT_DVD "iSCSI DVD" #ifndef USE_LU_TAPE_DLT8000 #define DEFAULT_LU_PRODUCT_TAPE "iSCSI TAPE" #else #define DEFAULT_LU_PRODUCT_TAPE "DLT8000" #endif /* !USE_LU_TAPE_DLT8000 */ #define DEFAULT_LU_REVISION "0001" #define DEFAULT_LU_REVISION_DISK DEFAULT_LU_REVISION #define DEFAULT_LU_REVISION_DVD DEFAULT_LU_REVISION #ifndef USE_LU_TAPE_DLT8000 #define DEFAULT_LU_REVISION_TAPE DEFAULT_LU_REVISION #else #define DEFAULT_LU_REVISION_TAPE "C001" #endif /* !USE_LU_TAPE_DLT8000 */ #define MAX_INQUIRY_SERIAL 16 #define ISTGT_LU_WORK_BLOCK_SIZE (1ULL * 1024ULL * 1024ULL) #define ISTGT_LU_MAX_WRITE_CACHE_SIZE (8ULL * 1024ULL * 1024ULL) #define ISTGT_LU_MEDIA_SIZE_MIN (1ULL * 1024ULL * 1024ULL) #define ISTGT_LU_MEDIA_EXTEND_UNIT (256ULL * 1024ULL * 1024ULL) #define ISTGT_LU_1GB (1ULL * 1024ULL * 1024ULL * 1024ULL) #define ISTGT_LU_1MB (1ULL * 1024ULL * 1024ULL) typedef enum { ISTGT_LU_FLAG_MEDIA_READONLY = 0x00000001, ISTGT_LU_FLAG_MEDIA_AUTOSIZE = 0x00000002, ISTGT_LU_FLAG_MEDIA_EXTEND = 0x00000010, ISTGT_LU_FLAG_MEDIA_DYNAMIC = 0x00000020, } ISTGT_LU_FLAG; typedef enum { ISTGT_LU_TYPE_NONE = 0, ISTGT_LU_TYPE_PASS = 1, ISTGT_LU_TYPE_DISK = 2, ISTGT_LU_TYPE_DVD = 3, ISTGT_LU_TYPE_TAPE = 4, } ISTGT_LU_TYPE; typedef enum { ISTGT_LU_LUN_TYPE_NONE = 0, ISTGT_LU_LUN_TYPE_DEVICE = 1, ISTGT_LU_LUN_TYPE_STORAGE = 2, ISTGT_LU_LUN_TYPE_REMOVABLE = 3, ISTGT_LU_LUN_TYPE_SLOT = 4, } ISTGT_LU_LUN_TYPE; typedef struct istgt_lu_device_t { char *file; } ISTGT_LU_DEVICE; typedef struct istgt_lu_storage_t { int fd; char *file; uint64_t size; } ISTGT_LU_STORAGE; typedef struct istgt_lu_removable_t { int type; int id; int flags; int fd; char *file; uint64_t size; } ISTGT_LU_REMOVABLE; typedef struct istgt_lu_slot_t { int maxslot; int present[MAX_LU_LUN_SLOT]; int flags[MAX_LU_LUN_SLOT]; char *file[MAX_LU_LUN_SLOT]; uint64_t size[MAX_LU_LUN_SLOT]; } ISTGT_LU_SLOT; typedef struct istgt_lu_lun_t { int type; union { ISTGT_LU_DEVICE device; ISTGT_LU_STORAGE storage; ISTGT_LU_REMOVABLE removable; ISTGT_LU_SLOT slot; } u; int rotationrate; int formfactor; int readcache; int writecache; char *serial; void *spec; } ISTGT_LU_LUN; typedef ISTGT_LU_LUN *ISTGT_LU_LUN_Ptr; typedef struct istgt_lu_tsih_t { int tag; uint16_t tsih; char *initiator_port; } ISTGT_LU_TSIH; typedef enum { AAS_ACTIVE_OPTIMIZED = 0x00, AAS_ACTIVE_NON_OPTIMIZED = 0x01, AAS_STANDBY = 0x02, AAS_UNAVAILABLE = 0x03, AAS_TRANSITIONING = 0x0F, AAS_STATUS_NO = 0x0000, AAS_STATUS_STPG = 0x0100, AAS_STATUS_IMPLICIT = 0x0200, } ISTGT_LU_AAS; typedef struct istgt_lu_map_t { int pg_tag; int pg_aas; int ig_tag; } ISTGT_LU_MAP; typedef struct istgt_lu_t { int num; char *name; char *alias; char *inq_vendor; char *inq_product; char *inq_revision; char *inq_serial; ISTGT_Ptr istgt; ISTGT_STATE state; pthread_mutex_t mutex; pthread_mutex_t state_mutex; pthread_mutex_t queue_mutex; pthread_cond_t queue_cond; pthread_t thread; uint16_t last_tsih; int no_auth_chap; int auth_chap; int auth_chap_mutual; int auth_group; int header_digest; int data_digest; int MaxOutstandingR2T; int DefaultTime2Wait; int DefaultTime2Retain; int FirstBurstLength; int MaxBurstLength; int MaxRecvDataSegmentLength; int InitialR2T; int ImmediateData; int DataPDUInOrder; int DataSequenceInOrder; int ErrorRecoveryLevel; int type; int online; int readonly; int blocklen; int queue_depth; int maxlun; ISTGT_LU_LUN lun[MAX_LU_LUN]; int maxtsih; ISTGT_LU_TSIH tsih[MAX_LU_TSIH]; int maxmap; ISTGT_LU_MAP map[MAX_LU_MAP]; } ISTGT_LU; typedef ISTGT_LU *ISTGT_LU_Ptr; typedef struct istgt_lu_cmd_t { struct iscsi_pdu_t *pdu; ISTGT_LU_Ptr lu; int I_bit; int F_bit; int R_bit; int W_bit; int Attr_bit; uint64_t lun; uint32_t task_tag; uint32_t transfer_len; uint32_t CmdSN; uint8_t *cdb; uint8_t *iobuf; int iobufsize; uint8_t *data; int data_len; int alloc_len; int status; uint8_t *sense_data; int sense_data_len; int sense_alloc_len; } ISTGT_LU_CMD; typedef ISTGT_LU_CMD *ISTGT_LU_CMD_Ptr; enum { ISTGT_LU_TASK_RESULT_IMMEDIATE = 0, ISTGT_LU_TASK_RESULT_QUEUE_OK = 1, ISTGT_LU_TASK_RESULT_QUEUE_FULL = 2, } ISTGT_LU_TASK_RESULT; enum { ISTGT_LU_TASK_RESPONSE = 0, ISTGT_LU_TASK_REQPDU = 1, } ISTGT_LU_TASK_TYPE; typedef struct istgt_lu_task_t { int type; struct istgt_conn_t *conn; char initiator_name[MAX_INITIATOR_NAME]; char initiator_port[MAX_INITIATOR_NAME]; ISTGT_LU_CMD lu_cmd; int lun; pthread_t thread; int use_cond; pthread_mutex_t trans_mutex; pthread_cond_t trans_cond; pthread_cond_t exec_cond; time_t create_time; int condwait; int dup_iobuf; uint8_t *iobuf; uint8_t *data; uint8_t *sense_data; int offset; int req_execute; int req_transfer_out; int error; int abort; int execute; int complete; int lock; } ISTGT_LU_TASK; typedef ISTGT_LU_TASK *ISTGT_LU_TASK_Ptr; #endif /* ISTGT_LU_H */ istgt-20111008/src/istgt_lu_tape.c000644 000000 000000 00000422074 11530752361 017013 0ustar00rootwheel000000 000000 /* * Copyright (C) 2008-2011 Daisuke Aoyama . * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #include #include #include #include #include #ifdef HAVE_UUID_H #include #endif #include #include #include "istgt.h" #include "istgt_ver.h" #include "istgt_log.h" #include "istgt_conf.h" #include "istgt_sock.h" #include "istgt_misc.h" #include "istgt_iscsi.h" #include "istgt_lu.h" #include "istgt_proto.h" #include "istgt_scsi.h" #define TAPE_DEBUG //#define ISTGT_TRACE_TAPE #define DENSITY_DFLT (TAPE_DENSITY_DEFAULT) //#define MEDIATYPE_DFLT (TAPE_MEDIATYPE_DLT_III) //#define DENSITY_DFLT (TAPE_DENSITY_DLT_III) #define MEDIATYPE_DFLT (TAPE_MEDIATYPE_DLT_IV) //#define DENSITY_DFLT (TAPE_DENSITY_DLT_IV) //#define MEDIATYPE_DFLT (TAPE_MEDIATYPE_SDLT_I) //#define DENSITY_DFLT (TAPE_DENSITY_SDLT_I) //#define MEDIATYPE_DFLT (TAPE_MEDIATYPE_LTO4) //#define DENSITY_DFLT (TAPE_DENSITY_LTO4) /* Block Alignment for emulation tape */ #define TAPE_BLOCKLEN 512 #define TAPE_ALIGNMENT 8 #define COMPRESSION_DFLT 1 #define ISCSI_DLT 0 #define ISCSI_LTO 1 #define TAPE_VENDOR "QUANTUM" #define TAPE_PRODUCT "DLT8000" #define TAPE_REVISION "CX01" /* servo + r/w */ #define TAPE_MODULE_REV "C001" #if 0 #define TAPE_PRODUCT "DLT4000" #define TAPE_REVISION "CD01" #define TAPE_MODULE_REV "C001" #endif #if 0 /* Quantum DLT8000 */ #define TAPE_MAXIMUM_BLOCK_LENGTH 0x0ffffe #define TAPE_MINIMUM_BLOCK_LENGTH 0x000000 #define TAPE_WRITE_DELAY 200 /* x 100ms */ /* for multiple of 4bytes */ #define TAPE_MAXIMUM_BLOCK_LENGTH 0xfffffc #define TAPE_MINIMUM_BLOCK_LENGTH 0x000004 #endif /* for multiple of 8bytes */ #define TAPE_MAXIMUM_BLOCK_LENGTH 0xfffff8 #define TAPE_MINIMUM_BLOCK_LENGTH 0x000008 //#define TAPE_WRITE_DELAY 0x000f /* x 100ms */ #define TAPE_WRITE_DELAY 200 /* x 100ms */ #define TAPE_COMP_ALGORITHM 0x10 /* IBM IDRC */ #define TAPE_MEDIATYPE_NONE 0x00 #define TAPE_MEDIATYPE_DLT_CL 0x81 #define TAPE_MEDIATYPE_DLT_III 0x83 #define TAPE_MEDIATYPE_DLT_IIIXT 0x84 #define TAPE_MEDIATYPE_DLT_IV 0x85 #define TAPE_MEDIATYPE_SDLT_I 0x86 #define TAPE_MEDIATYPE_SDLT_II 0x87 #define TAPE_MEDIATYPE_DLT_S4 0x91 #define TAPE_MEDIATYPE_LTO1 0x18 #define TAPE_MEDIATYPE_LTO2 0x28 #define TAPE_MEDIATYPE_LTO3 0x38 #define TAPE_MEDIATYPE_LTO4 0x48 #define TAPE_DENSITY_DEFAULT 0x00 #define TAPE_DENSITY_DLT_III 0x19 #define TAPE_DENSITY_DLT_IV20 0x1a #define TAPE_DENSITY_DLT_IV35 0x1b #define TAPE_DENSITY_DLT_IV 0x41 #define TAPE_DENSITY_SDLT_I 0x49 #define TAPE_DENSITY_SDLT_II 0x4a #define TAPE_DENSITY_DLT_S4 0x4b #define TAPE_DENSITY_LTO1 0x40 #define TAPE_DENSITY_LTO2 0x42 #define TAPE_DENSITY_LTO3 0x44 #define TAPE_DENSITY_LTO4 0x46 #define CTLBLOCKLEN (128*1024) #define CTLMAGIC "ISVTCTRL" #define CTLMAGICLEN 8 #define CTLVERSION 0ULL #define CTLENDIAN 0x1122334455667788ULL #define MARK_END 0xffffffffffffffffULL #define MARK_EOD 0xfffffffffffffffeULL #define LBPOS_INVALID 0xffffffffffffffffULL #define LBPOS_MAX 0xfffffffffffffffeULL typedef struct tape_markpos_t { uint64_t lbpos; /* logical position */ uint64_t offset; /* physical position */ uint64_t prev; /* previous position if not zero */ uint64_t junk1; } tape_markpos_t; /* Control Block = 128K */ #define MAX_FILEMARKS (1024) typedef struct tape_ctlblock_t { /* 16k block 0-2 */ uint8_t magic[8]; /* 'ISVTCTRL' (network order) */ uint64_t endian; /* endian ID = 0x1122334455667788ULL */ uint64_t version; /* version = 0 */ uint64_t ctlblocklen; /* ctlblocklen = 128K */ uint64_t blocklen; /* blocklen = 512 */ uint64_t marklen; /* marklen = 128 */ uint64_t alignment; /* alignment = 8 */ uint64_t allocate; /* allocate = 0 */ uint64_t type; /* media type = default */ uint64_t id; /* media ID = empty */ uint64_t size; /* media size = empty */ uint64_t junk1; uint64_t reserve0[512-12]; /* room for 4K(8x512) */ tape_markpos_t marks[MAX_FILEMARKS]; /* marks[0] = BOT, ..., EOT 32K */ uint8_t reserve2[(16*1024) - (8*512)]; /* 16k block 3-7 */ uint8_t reserve3[(16*1024)]; uint8_t reserve4[(16*1024)]; uint8_t reserve5[(16*1024)]; uint8_t reserve6[(16*1024)]; uint8_t reserve7[(16*1024)]; } tape_ctlblock_t; /* physical marker in virtual tape */ #define MARK_LENGTH 128 #define MARK_MAXLENGTH (TAPE_BLOCKLEN) #define MARK_MAGICLEN 8 #define MARK_VERSION 0ULL #define MARK_ENDIAN 0x1122334455667788ULL #define MARK_BOTMAGIC "ISVTBOTB" #define MARK_EOTMAGIC "ISVTEOTB" #define MARK_EOFMAGIC "ISVTEOFB" #define MARK_EODMAGIC "ISVTEODB" #define MARK_DATAMAGIC "ISVTDATA" #define MARK_COMPALGO_NONE 0 /* Mark Block = 128B */ typedef struct tape_markblock_t { uint8_t magic[8]; /* 'ISVT'+ 'BOTB' / 'DATA' / 'EOFB' */ uint64_t endian; /* endian ID = 0x1122334455667788ULL */ uint64_t version; /* version = 0 */ uint64_t marklen; /* marklen = 128 */ uint64_t lblen; /* logical block length */ uint64_t lbpos; /* logical block position */ uint64_t offset; /* self physical offset */ uint64_t prev; /* previous offset if non zero */ uint64_t compalgo; /* compression algorithm (0=none) */ uint64_t vtcompalgo; /* VT compression algorithm (0=none) */ uint64_t vtdecomplen; /* VT decompression length */ uint64_t junk1; /* reserved */ uint64_t reserve[16-12]; /* 128B(8x16) */ } tape_markblock_t; typedef struct istgt_lu_tape_t { ISTGT_LU_Ptr lu; int num; int lun; int fd; const char *file; uint64_t size; uint64_t blocklen; uint64_t blockcnt; #ifdef HAVE_UUID_H uuid_t uuid; #endif /* HAVE_UUID_H */ /* flags */ int mflags; tape_ctlblock_t *ctlblock; /* control block */ tape_markblock_t *markblock; /* mark block */ uint64_t lblen; /* logical block length for fixed */ uint64_t lbpos; /* logical block position */ uint64_t offset; /* physical offset in virtual tape */ uint64_t prev; /* previous offset if not zero */ int index; /* current maker index */ int compalgo; /* compression algorithme */ int vtcompalgo; /* compression algorithme in vtape */ /* pending flags */ int need_savectl; int need_writeeod; /* media state */ volatile int mload; volatile int mchanged; volatile int mwait; /* mode flags */ volatile int lock; int compression; int bot; int eof; int eod; int eom; /* SCSI sense code */ volatile int sense; /* command information */ uint32_t info; } ISTGT_LU_TAPE; #define BUILD_SENSE(SK,ASC,ASCQ) \ do { \ *sense_len = \ istgt_lu_tape_build_sense_data(spec, sense_data, \ ISTGT_SCSI_SENSE_ ## SK, \ (ASC), (ASCQ)); \ } while (0) static int istgt_lu_tape_save_ctlblock(ISTGT_LU_TAPE *spec); static int istgt_lu_tape_allocate(ISTGT_LU_TAPE *spec); static int istgt_lu_tape_build_sense_data(ISTGT_LU_TAPE *spec, uint8_t *data, int sk, int asc, int ascq); static int istgt_lu_tape_open(ISTGT_LU_TAPE *spec, int flags, int mode) { int rc; rc = open(spec->file, flags, mode); if (rc < 0) { return -1; } spec->fd = rc; return 0; } static int istgt_lu_tape_close(ISTGT_LU_TAPE *spec) { int rc; if (spec->fd == -1) return 0; rc = close(spec->fd); if (rc < 0) { return -1; } spec->fd = -1; return 0; } static int64_t istgt_lu_tape_seek(ISTGT_LU_TAPE *spec, uint64_t offset) { off_t rc; rc = lseek(spec->fd, (off_t) offset, SEEK_SET); if (rc < 0) { return -1; } return 0; } static int64_t istgt_lu_tape_read(ISTGT_LU_TAPE *spec, void *buf, uint64_t nbytes) { int64_t rc; rc = (int64_t) read(spec->fd, buf, (size_t) nbytes); if (rc < 0) { return -1; } return rc; } static int64_t istgt_lu_tape_write(ISTGT_LU_TAPE *spec, const void *buf, uint64_t nbytes) { int64_t rc; rc = (int64_t) write(spec->fd, buf, (size_t) nbytes); if (rc < 0) { return -1; } return rc; } static int64_t istgt_lu_tape_sync(ISTGT_LU_TAPE *spec, uint64_t offset, uint64_t nbytes) { int64_t rc; rc = (int64_t) fsync(spec->fd); if (rc < 0) { return -1; } return rc; } #if 0 static uint64_t swap_uint64(uint64_t val) { uint64_t r; int i; r = 0; for (i = 0; i < sizeof(uint64_t); i++) { r |= val & 0xffULL; r <<= 8; val >>= 8; } return r; } #endif #define SWAP_UINT64(D) \ ((( (D) >> (56 - 0 )) & 0x00000000000000ffULL) \ | (((D) << (56 - 0 )) & 0xff00000000000000ULL) \ | (((D) >> (48 - 8 )) & 0x000000000000ff00ULL) \ | (((D) << (48 - 8 )) & 0x00ff000000000000ULL) \ | (((D) >> (40 - 16)) & 0x0000000000ff0000ULL) \ | (((D) << (40 - 16)) & 0x0000ff0000000000ULL) \ | (((D) >> (32 - 24)) & 0x00000000ff000000ULL) \ | (((D) << (32 - 24)) & 0x000000ff00000000ULL)) static int istgt_lu_tape_read_native_mark(ISTGT_LU_TAPE *spec, tape_markblock_t *mbp) { uint64_t marklen; uint64_t *lp; int64_t rc; int i; marklen = spec->ctlblock->marklen; rc = istgt_lu_tape_read(spec, mbp, marklen); if (rc < 0 || rc != marklen) { ISTGT_ERRLOG("lu_tape_read() failed: rc %d\n", rc); return -1; } if (mbp->endian != MARK_ENDIAN) { /* convert byte order but except magic */ lp = (uint64_t *) mbp; for (i = 1; i < marklen / sizeof(uint64_t); i++) { lp[i] = SWAP_UINT64(lp[i]); } } return 0; } static int istgt_lu_tape_write_native_mark(ISTGT_LU_TAPE *spec, tape_markblock_t *mbp) { uint64_t marklen; int64_t rc; marklen = spec->ctlblock->marklen; rc = istgt_lu_tape_write(spec, mbp, marklen); if (rc != marklen) { ISTGT_ERRLOG("lu_tape_write() failed at offset %" PRIu64 ", size %" PRIu64 "\n", spec->offset, spec->size); return -1; } return 0; } static int istgt_lu_tape_write_padding(ISTGT_LU_TAPE *spec, uint8_t *data) { uint64_t tape_leader; uint64_t offset; uint64_t alignment, padlen; int64_t rc; tape_leader = spec->ctlblock->ctlblocklen; offset = spec->offset; alignment = spec->ctlblock->alignment; if (offset % alignment) { padlen = alignment; padlen -= offset % alignment; memset(data, 0, alignment); if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } rc = istgt_lu_tape_write(spec, data, padlen); if (rc < 0 || rc != padlen) { ISTGT_ERRLOG("lu_tape_write() failed\n"); return -1; } offset += padlen; spec->offset = offset; } return 0; } static int istgt_lu_tape_write_eof(ISTGT_LU_TAPE *spec, int count, uint8_t *data) { tape_markblock_t *mbp; uint64_t tape_leader; uint64_t lbpos, offset, prev, version, marklen; int index_i; int i; if (count <= 0) { // flush buffer return 0; } if (istgt_lu_tape_write_padding(spec, data) < 0) { ISTGT_ERRLOG("lu_tape_write_padding() failed\n"); return -1; } tape_leader = spec->ctlblock->ctlblocklen; lbpos = spec->lbpos; offset = spec->offset; prev = spec->prev; index_i = spec->index; version = spec->ctlblock->version; marklen = spec->ctlblock->marklen; /* prepare mark */ mbp = (tape_markblock_t *) data; memset(mbp, 0, marklen); memcpy(mbp->magic, MARK_EOFMAGIC, MARK_MAGICLEN); mbp->endian = MARK_ENDIAN; mbp->version = MARK_VERSION; mbp->marklen = marklen; mbp->lblen = 0ULL; mbp->compalgo = 0ULL; mbp->vtcompalgo = 0ULL; mbp->vtdecomplen = 0ULL; /* seek to current physical position */ if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } /* write EOF N blocks */ for (i = 0; i < count; i++) { mbp->lbpos = lbpos; mbp->offset = offset; mbp->prev = prev; index_i++; spec->ctlblock->marks[index_i].lbpos = lbpos; spec->ctlblock->marks[index_i].offset = offset; spec->ctlblock->marks[index_i].prev = prev; spec->ctlblock->marks[index_i + 1].lbpos = MARK_END; spec->ctlblock->marks[index_i + 1].offset = MARK_END; spec->ctlblock->marks[index_i + 1].prev = offset + marklen; spec->index = index_i; spec->offset = offset; if (istgt_lu_tape_write_native_mark(spec, mbp) < 0) { ISTGT_ERRLOG("istgt_lu_tape_write_native_mark() failed\n"); spec->prev = 0ULL; return -1; } lbpos++; prev = offset; offset += marklen; /* update information */ spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; spec->eof = 1; } return 0; } static int istgt_lu_tape_write_bot(ISTGT_LU_TAPE *spec, uint8_t *data) { tape_markblock_t *mbp; uint64_t tape_leader; uint64_t lbpos, offset, prev, version, marklen; int index_i; tape_leader = spec->ctlblock->ctlblocklen; lbpos = 0ULL; offset = 0ULL; prev = 0ULL; index_i = 0ULL; version = spec->ctlblock->version; marklen = spec->ctlblock->marklen; /* prepare mark */ mbp = (tape_markblock_t *) data; memset(mbp, 0, marklen); memcpy(mbp->magic, MARK_BOTMAGIC, MARK_MAGICLEN); mbp->endian = MARK_ENDIAN; mbp->version = MARK_VERSION; mbp->marklen = marklen; mbp->lblen = 0ULL; mbp->compalgo = 0ULL; mbp->vtcompalgo = 0ULL; mbp->vtdecomplen = 0ULL; /* seek to current physical position */ if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } /* write BOT block */ mbp->lbpos = lbpos; mbp->offset = offset; mbp->prev = prev; index_i++; spec->ctlblock->marks[index_i].lbpos = lbpos; spec->ctlblock->marks[index_i].offset = offset; spec->ctlblock->marks[index_i].prev = prev; spec->ctlblock->marks[index_i + 1].lbpos = MARK_END; spec->ctlblock->marks[index_i + 1].offset = MARK_END; spec->ctlblock->marks[index_i + 1].prev = offset + marklen; spec->index = index_i; spec->offset = offset; if (istgt_lu_tape_write_native_mark(spec, mbp) < 0) { ISTGT_ERRLOG("lu_tape_write_native_mark() failed\n"); spec->prev = 0ULL; return -1; } lbpos++; prev = offset; offset += marklen; /* update information */ spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; return 0; } static int istgt_lu_tape_write_eod(ISTGT_LU_TAPE *spec, uint8_t *data) { tape_markblock_t *mbp; uint64_t tape_leader; uint64_t lbpos, offset, prev, version, marklen; int index_i; tape_leader = spec->ctlblock->ctlblocklen; lbpos = spec->lbpos; offset = spec->offset; prev = spec->prev; index_i = spec->index; version = spec->ctlblock->version; marklen = spec->ctlblock->marklen; /* prepare mark */ mbp = (tape_markblock_t *) data; memset(mbp, 0, marklen); memcpy(mbp->magic, MARK_EODMAGIC, MARK_MAGICLEN); mbp->endian = MARK_ENDIAN; mbp->version = MARK_VERSION; mbp->marklen = marklen; mbp->lblen = 0ULL; mbp->compalgo = 0ULL; mbp->vtcompalgo = 0ULL; mbp->vtdecomplen = 0ULL; /* seek to current physical position */ if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } /* write EOD block */ mbp->lbpos = lbpos; mbp->offset = offset; mbp->prev = prev; if (istgt_lu_tape_write_native_mark(spec, mbp) < 0) { ISTGT_ERRLOG("lu_tape_write_native_mark() failed\n"); return -1; } /* no update information */ return 0; } static int istgt_lu_tape_write_media_check(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t request_len) { uint64_t tape_leader; uint64_t extendsize; uint64_t mediasize; uint64_t offset; int data_len; tape_leader = spec->ctlblock->ctlblocklen; mediasize = spec->size; offset = spec->offset; /* writable media? */ if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { /* WRITE PROTECTED */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* always keep control block */ if (mediasize < tape_leader) { /* INTERNAL TARGET FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x44, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } mediasize -= tape_leader; /* request can store? */ if (request_len > mediasize || offset > mediasize - request_len) { /* determine extend size */ extendsize = request_len / ISTGT_LU_MEDIA_EXTEND_UNIT; extendsize *= ISTGT_LU_MEDIA_EXTEND_UNIT; if (request_len % ISTGT_LU_MEDIA_EXTEND_UNIT) { extendsize += ISTGT_LU_MEDIA_EXTEND_UNIT; } /* can handle? */ if (mediasize < MARK_END - 1 - tape_leader - extendsize) { if (spec->mflags & ISTGT_LU_FLAG_MEDIA_DYNAMIC) { /* OK dynamic allocation */ mediasize += extendsize; } else if (spec->mflags & ISTGT_LU_FLAG_MEDIA_EXTEND) { /* OK extend media size */ mediasize += extendsize; } else { /* no space virtual EOM */ goto eom_error; } } else { eom_error: /* physical EOM */ spec->eom = 1; /* END-OF-PARTITION/MEDIUM DETECTED */ /* VOLUME OVERFLOW */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_VOLUME_OVERFLOW, 0x00, 0x02); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } } /* update information */ spec->size = tape_leader + mediasize; /* complete check, ready to write */ return 0; } static int istgt_lu_tape_read_media_check(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t request_len) { uint64_t tape_leader; uint64_t mediasize; uint64_t offset; int data_len; tape_leader = spec->ctlblock->ctlblocklen; mediasize = spec->size; offset = spec->offset; /* always keep control block */ if (mediasize < tape_leader) { /* INTERNAL TARGET FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x44, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } mediasize -= tape_leader; /* request can seek? */ if (request_len > mediasize || offset > mediasize - request_len) { /* physical EOM */ spec->eom = 1; /* END-OF-PARTITION/MEDIUM DETECTED */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_MEDIUM_ERROR, 0x00, 0x02); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* complete check, ready to read */ return 0; } static int istgt_lu_tape_prepare_offset(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { uint64_t lbpos, offset, prev, marklen; int index_i; lbpos = spec->lbpos; offset = spec->offset; prev = spec->prev; index_i = spec->index; marklen = spec->ctlblock->marklen; /* position to logical block zero */ if (spec->bot) { spec->bot = 0; spec->eof = spec->eod = spec->eom = 0; offset = 0; prev = offset; offset += marklen; lbpos++; } if (spec->eom || offset == MARK_END) { spec->eom = 1; spec->bot = spec->eof = spec->eod = 0; } /* update information */ spec->index = index_i; spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; return 0; } static int istgt_lu_tape_write_pending_data(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { uint64_t marklen; int data_len; if (spec->need_savectl) { if (istgt_lu_tape_save_ctlblock(spec) < 0) { ISTGT_ERRLOG("lu_tape_save_ctlblock() failed\n"); io_failure: /* INTERNAL TARGET FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x44, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } spec->need_savectl = 0; } if (spec->need_writeeod) { marklen = spec->ctlblock->marklen; if (istgt_lu_tape_write_media_check(spec, conn, lu_cmd, marklen) < 0) { goto io_failure; } if (istgt_lu_tape_write_eod(spec, lu_cmd->data) < 0) { ISTGT_ERRLOG("lu_tape_write_eod() failed\n"); goto io_failure; } spec->need_writeeod = 0; } return 0; } static int istgt_lu_tape_rewind(ISTGT_LU_TAPE *spec) { uint64_t lbpos, offset, prev; int index_i; /* position to BOT */ spec->bot = 1; spec->eof = spec->eod = spec->eom = 0; index_i = 0; lbpos = spec->ctlblock->marks[index_i].lbpos; offset = spec->ctlblock->marks[index_i].offset; prev = spec->ctlblock->marks[index_i].prev; /* update information */ spec->index = index_i; spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; return 0; } static int istgt_lu_tape_load_ctlblock(ISTGT_LU_TAPE *spec) { int64_t rc; if (istgt_lu_tape_seek(spec, 0) == -1) { return -1; } rc = istgt_lu_tape_read(spec, spec->ctlblock, CTLBLOCKLEN); if (rc < 0 || rc != CTLBLOCKLEN) { return -1; } return rc; } static int istgt_lu_tape_save_ctlblock(ISTGT_LU_TAPE *spec) { int64_t rc; if (istgt_lu_tape_seek(spec, 0) == -1) { return -1; } rc = istgt_lu_tape_write(spec, spec->ctlblock, spec->ctlblock->ctlblocklen); if (rc < 0 || rc != spec->ctlblock->ctlblocklen) { return -1; } return rc; } static int istgt_lu_tape_init_ctlblock(ISTGT_LU_TAPE *spec, int newfile) { tape_ctlblock_t *cbp; uint64_t *lp; int rc; int i; cbp = spec->ctlblock; rc = istgt_lu_tape_load_ctlblock(spec); if (rc < 0) { return -1; } if (memcmp(cbp->magic, CTLMAGIC, CTLMAGICLEN) != 0) { if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY) || !newfile) { ISTGT_ERRLOG("Can not initialize \"%s\"\n", spec->file); return -1; } /* initialize control block */ memset(cbp, 0, CTLBLOCKLEN); memcpy(cbp->magic, CTLMAGIC, CTLMAGICLEN); cbp->marks[0].offset = 0ULL; cbp->marks[0].lbpos = 0ULL; cbp->marks[0].prev = 0ULL; cbp->marks[1].offset = MARK_END; cbp->marks[1].lbpos = MARK_END; cbp->marks[1].prev = 0ULL; cbp->endian = CTLENDIAN; cbp->version = CTLVERSION; cbp->ctlblocklen = (uint64_t) CTLBLOCKLEN; cbp->blocklen = (uint64_t) TAPE_BLOCKLEN; cbp->marklen = (uint64_t) MARK_LENGTH; cbp->alignment = (uint64_t) TAPE_ALIGNMENT; cbp->allocate = 0ULL; cbp->type = 0ULL; cbp->id = 0ULL; cbp->size = 0ULL; rc = istgt_lu_tape_save_ctlblock(spec); if (rc < 0) { ISTGT_ERRLOG("lu_tape_save_ctlblock() failed\n"); return -1; } rc = istgt_lu_tape_write_bot(spec, (uint8_t *) spec->markblock); if (rc < 0) { ISTGT_ERRLOG("lu_tape_write_bot() failed\n"); return -1; } rc = istgt_lu_tape_write_eod(spec, (uint8_t *) spec->markblock); if (rc < 0) { ISTGT_ERRLOG("lu_tape_write_eod() failed\n"); return -1; } } else { if (cbp->endian != CTLENDIAN) { /* convert byte order but except magic */ lp = (uint64_t *) cbp; for (i = 1; i < CTLBLOCKLEN / sizeof(uint64_t); i++) { lp[i] = SWAP_UINT64(lp[i]); } } if (cbp->ctlblocklen == 0ULL || cbp->blocklen == 0ULL || cbp->marklen == 0ULL || cbp->alignment == 0ULL) { ISTGT_ERRLOG("bad length\n"); return -1; } if (cbp->version > CTLVERSION) { ISTGT_ERRLOG("unsupported tape version 0x%"PRIx64"\n", cbp->version); return -1; } if (cbp->marklen > MARK_MAXLENGTH) { ISTGT_ERRLOG("marklen is too long\n"); return -1; } } return 0; } int istgt_lu_tape_media_present(ISTGT_LU_TAPE *spec) { if (spec->mload) { return 1; } return 0; } int istgt_lu_tape_media_lock(ISTGT_LU_TAPE *spec) { if (spec->lock) { return 1; } return 0; } int istgt_lu_tape_load_media(ISTGT_LU_TAPE *spec) { ISTGT_LU_Ptr lu; int flags; int newfile; int rc; if (istgt_lu_tape_media_present(spec)) { /* media present */ return -1; } if (spec->mchanged) { /* changed soon */ return -1; } lu = spec->lu; if (lu->lun[spec->lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: not removable\n", lu->num); return -1; } if (strcasecmp(lu->lun[spec->lun].u.removable.file, "/dev/null") == 0) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: empty\n", lu->num); spec->file = NULL; spec->size = 0; spec->mflags = 0; spec->blocklen = TAPE_BLOCKLEN; spec->blockcnt = spec->size / spec->blocklen; spec->compalgo = TAPE_COMP_ALGORITHM; spec->vtcompalgo = MARK_COMPALGO_NONE; spec->compression = COMPRESSION_DFLT; spec->lblen = 0ULL; /* default to variable length */ spec->index = 0; /* position to BOT */ spec->lbpos = 0ULL; spec->offset = 0ULL; spec->prev = 0ULL; spec->bot = 0; spec->eof = spec->eod = spec->eom = 0; spec->prev = spec->offset; spec->need_savectl = 0; spec->need_writeeod = 0; return 0; } spec->file = lu->lun[spec->lun].u.removable.file; spec->size = lu->lun[spec->lun].u.removable.size; spec->mflags = lu->lun[spec->lun].u.removable.flags; spec->blocklen = TAPE_BLOCKLEN; spec->blockcnt = spec->size / spec->blocklen; spec->compalgo = TAPE_COMP_ALGORITHM; spec->vtcompalgo = MARK_COMPALGO_NONE; spec->compression = COMPRESSION_DFLT; spec->lblen = 0ULL; /* default to variable length */ spec->index = 0; /* position to BOT */ spec->lbpos = 0ULL; spec->offset = 0ULL; spec->prev = 0ULL; spec->bot = 1; spec->eof = spec->eod = spec->eom = 0; spec->prev = spec->offset; spec->need_savectl = 0; spec->need_writeeod = 0; spec->mload = 0; spec->mchanged = 1; spec->mwait = 3; if (access(spec->file, W_OK) != 0) { if (errno != ENOENT) { spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY; } } else { struct stat st; rc = stat(spec->file, &st); if (rc != 0 || !S_ISREG(st.st_mode)) { spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY; } else { if ((st.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH)) == 0) { spec->mflags |= ISTGT_LU_FLAG_MEDIA_READONLY; } } } if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { flags = O_RDONLY; } else { flags = O_RDWR; } newfile = 0; rc = istgt_lu_tape_open(spec, flags, 0666); if (rc < 0) { /* new file? */ newfile = 1; if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { flags = O_RDONLY; } else { flags = (O_CREAT | O_EXCL | O_RDWR); } rc = istgt_lu_tape_open(spec, flags, 0666); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: open error\n", lu->num, spec->lun); return -1; } if (lu->lun[spec->lun].u.removable.size < ISTGT_LU_MEDIA_SIZE_MIN) { lu->lun[spec->lun].u.removable.size = ISTGT_LU_MEDIA_SIZE_MIN; } } if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { /* readonly */ } else { if (newfile == 0) { /* existing file check */ if (istgt_lu_tape_init_ctlblock(spec, newfile) < 0) { ISTGT_ERRLOG("lu_tape_init_ctlblock() failed\n"); return -1; } } rc = istgt_lu_tape_allocate(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: allocate error\n", lu->num, spec->lun); return -1; } } /* initialize filemarks */ if (istgt_lu_tape_init_ctlblock(spec, newfile) < 0) { ISTGT_ERRLOG("lu_tape_init_ctlblock() failed\n"); return -1; } istgt_lu_tape_rewind(spec); return 0; } int istgt_lu_tape_unload_media(ISTGT_LU_TAPE *spec) { int rc; if (!istgt_lu_tape_media_present(spec) && !spec->mchanged) { /* media absent */ return 0; } if (istgt_lu_tape_media_lock(spec)) { return -1; } if (spec->need_savectl) { if (istgt_lu_tape_save_ctlblock(spec) < 0) { ISTGT_ERRLOG("lu_tape_save_ctlblock() failed\n"); return -1; } spec->need_savectl = 0; } if (spec->need_writeeod) { if (istgt_lu_tape_write_eod(spec, (uint8_t *) spec->markblock) < 0) { ISTGT_ERRLOG("write_eod() failed\n"); return -1; } spec->need_writeeod = 0; } if (!spec->lu->readonly && !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { rc = istgt_lu_tape_sync(spec, 0, spec->size); if (rc < 0) { ISTGT_ERRLOG("lu_tape_sync() failed\n"); return -1; } } rc = (int64_t) istgt_lu_tape_close(spec); if (rc < 0) { ISTGT_ERRLOG("lu_tape_close() failed\n"); return -1; } spec->file = NULL; spec->size = 0; spec->mflags = 0; spec->blocklen = TAPE_BLOCKLEN; spec->blockcnt = spec->size / spec->blocklen; spec->compalgo = TAPE_COMP_ALGORITHM; spec->vtcompalgo = MARK_COMPALGO_NONE; spec->compression = COMPRESSION_DFLT; spec->lblen = 0ULL; /* default to variable length */ spec->index = 0; /* position to BOT */ spec->lbpos = 0ULL; spec->offset = 0ULL; spec->prev = 0ULL; spec->bot = 0; spec->eof = spec->eod = spec->eom = 0; spec->prev = spec->offset; spec->need_savectl = 0; spec->need_writeeod = 0; spec->mload = 0; spec->mchanged = 0; spec->mwait = 3; return 0; } int istgt_lu_tape_change_media(ISTGT_LU_TAPE *spec, char *type, char *flags, char *file, char *size) { ISTGT_LU_Ptr lu; char *mfile; uint64_t msize; int mflags; int rc; if (istgt_lu_tape_media_lock(spec)) { return -1; } lu = spec->lu; if (lu->lun[spec->lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: not removable\n", lu->num); return -1; } if (strcmp(type, "-") == 0) { /* use VT image */ ; } else { ISTGT_ERRLOG("unsupported media type\n"); return -1; } mfile = xstrdup(file); mflags = istgt_lu_parse_media_flags(flags); msize = istgt_lu_parse_media_size(file, size, &mflags); rc = istgt_lu_tape_unload_media(spec); if (rc < 0) { return -1; } /* replace */ xfree(lu->lun[spec->lun].u.removable.file); lu->lun[spec->lun].u.removable.file = mfile; lu->lun[spec->lun].u.removable.size = msize; lu->lun[spec->lun].u.removable.flags = mflags; /* reload */ rc = istgt_lu_tape_load_media(spec); if (rc < 0) { (void) istgt_lu_tape_unload_media(spec); } if (spec->file == NULL) { (void) istgt_lu_tape_unload_media(spec); } spec->mwait = 5; return rc; } static int istgt_lu_tape_allocate(ISTGT_LU_TAPE *spec) { uint8_t *data; uint64_t fsize; uint64_t size; uint64_t blocklen; uint64_t offset; uint64_t nbytes; int64_t rc; size = spec->size; blocklen = spec->blocklen; nbytes = blocklen; data = xmalloc(nbytes); memset(data, 0, nbytes); fsize = istgt_lu_get_filesize(spec->file); if (fsize > size) { xfree(data); return 0; } offset = size - nbytes; rc = istgt_lu_tape_seek(spec, offset); if (rc == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); xfree(data); return -1; } rc = istgt_lu_tape_read(spec, data, nbytes); /* EOF is OK */ if (rc == -1) { ISTGT_ERRLOG("lu_tape_read() failed\n"); xfree(data); return -1; } rc = istgt_lu_tape_seek(spec, offset); if (rc == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); xfree(data); return -1; } rc = istgt_lu_tape_write(spec, data, nbytes); if (rc == -1 || rc != nbytes) { ISTGT_ERRLOG("lu_tape_write() failed\n"); xfree(data); return -1; } xfree(data); return 0; } int istgt_lu_tape_init(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { ISTGT_LU_TAPE *spec; uint64_t gb_size; uint64_t mb_size; #ifdef HAVE_UUID_H uint32_t status; #endif /* HAVE_UUID_H */ int mb_digit; int ro; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_tape_init\n"); if (sizeof(tape_ctlblock_t) != CTLBLOCKLEN) { ISTGT_ERRLOG("Invalid ctlblock len %" PRIu64 ".\n", sizeof(tape_ctlblock_t)); return -1; } printf("LU%d TAPE UNIT\n", lu->num); ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); lu->lun[i].spec = NULL; continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); return -1; } ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d removable\n", lu->num, i); spec = xmalloc(sizeof *spec); memset(spec, 0, sizeof *spec); spec->lu = lu; spec->num = lu->num; spec->lun = i; spec->fd = -1; #ifdef HAVE_UUID_H uuid_create(&spec->uuid, &status); if (status != uuid_s_ok) { ISTGT_ERRLOG("LU%d: LUN%d: uuid_create() failed\n", lu->num, i); xfree(spec); return -1; } #endif /* HAVE_UUID_H */ spec->ctlblock = xmalloc(CTLBLOCKLEN); spec->markblock = xmalloc(MARK_MAXLENGTH); spec->mload = 0; spec->mchanged = 0; spec->mwait = 0; rc = istgt_lu_tape_load_media(spec); if (rc < 0) { ISTGT_ERRLOG("lu_tape_load_media() failed\n"); xfree(spec->markblock); xfree(spec->ctlblock); xfree(spec); return -1; } if (spec->file != NULL) { /* initial state */ spec->mload = 1; spec->mchanged = 0; spec->mwait = 0; if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { ro = 1; } else { ro = 0; } printf("LU%d: LUN%d file=%s, size=%"PRIu64", flag=%s\n", lu->num, i, spec->file, spec->size, ro ? "ro" : "rw"); printf("LU%d: LUN%d %"PRIu64" blocks, %"PRIu64" bytes/block\n", lu->num, i, spec->blockcnt, spec->blocklen); gb_size = spec->size / ISTGT_LU_1GB; mb_size = (spec->size % ISTGT_LU_1GB) / ISTGT_LU_1MB; if (gb_size > 0) { mb_digit = (int) (((mb_size * 100) / 1024) / 10); printf("LU%d: LUN%d %"PRIu64".%dGB %sstorage for %s\n", lu->num, i, gb_size, mb_digit, lu->readonly ? "readonly " : "", lu->name); } else { printf("LU%d: LUN%d %"PRIu64"MB %sstorage for %s\n", lu->num, i, mb_size, lu->readonly ? "readonly " : "", lu->name); } } else { /* initial state */ spec->mload = 0; spec->mchanged = 0; spec->mwait = 0; printf("LU%d: LUN%d empty slot\n", lu->num, i); } lu->lun[i].spec = spec; } return 0; } int istgt_lu_tape_shutdown(ISTGT_Ptr istgt, ISTGT_LU_Ptr lu) { ISTGT_LU_CMD lu_cmd; ISTGT_LU_TAPE *spec; uint8_t *data; int alloc_len; int rc; int i; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "istgt_lu_tape_shutdown\n"); alloc_len = 65536; data = xmalloc(alloc_len); memset(&lu_cmd, 0, sizeof lu_cmd); lu_cmd.iobuf = data; lu_cmd.iobufsize = alloc_len; lu_cmd.data = data; lu_cmd.data_len = 0; lu_cmd.alloc_len = alloc_len; lu_cmd.sense_data = data; lu_cmd.sense_alloc_len = alloc_len; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d TargetName=%s\n", lu->num, lu->name); for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); continue; } if (lu->lun[i].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { ISTGT_ERRLOG("LU%d: unsupported type\n", lu->num); xfree(data); return -1; } spec = (ISTGT_LU_TAPE *) lu->lun[i].spec; /* flush pending data */ rc = istgt_lu_tape_write_pending_data(spec, NULL, &lu_cmd); if (rc < 0) { ISTGT_ERRLOG("lu_tape_write_pending_data() failed\n"); /* ignore error for other cleanup */ } if (!spec->lu->readonly && !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { rc = istgt_lu_tape_sync(spec, 0, spec->size); if (rc < 0) { //ISTGT_ERRLOG("LU%d: lu_tape_sync() failed\n", lu->num); /* ignore error */ } } rc = istgt_lu_tape_close(spec); if (rc < 0) { //ISTGT_ERRLOG("LU%d: lu_tape_close() failed\n", lu->num); /* ignore error */ } xfree(spec->ctlblock); xfree(spec->markblock); xfree(spec); lu->lun[i].spec = NULL; } xfree(data); return 0; } static int istgt_lu_tape_scsi_report_luns(ISTGT_LU_Ptr lu, CONN_Ptr conn, uint8_t *cdb, int sel, uint8_t *data, int alloc_len) { uint64_t fmt_lun, lun, method; int hlen = 0, len = 0; int i; if (alloc_len < 8) { return -1; } if (sel == 0x00) { /* logical unit with addressing method */ } else if (sel == 0x01) { /* well known logical unit */ } else if (sel == 0x02) { /* logical unit */ } else { return -1; } /* LUN LIST LENGTH */ DSET32(&data[0], 0); /* Reserved */ DSET32(&data[4], 0); hlen = 8; for (i = 0; i < lu->maxlun; i++) { if (lu->lun[i].type == ISTGT_LU_LUN_TYPE_NONE) { #if 0 ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "LU%d: LUN%d none\n", lu->num, i); #endif continue; } if (alloc_len - (hlen + len) < 8) { return -1; } lun = (uint64_t) i; if (lu->maxlun <= 0x0100) { /* below 256 */ method = 0x00U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (lun & 0x00ffU) << 48; } else if (lu->maxlun <= 0x4000U) { /* below 16384 */ method = 0x01U; fmt_lun = (method & 0x03U) << 62; fmt_lun |= (lun & 0x3fffU) << 48; } else { /* XXX */ fmt_lun = 0; } /* LUN */ DSET64(&data[hlen + len], fmt_lun); len += 8; } /* LUN LIST LENGTH */ DSET32(&data[0], len); return hlen + len; } static int istgt_lu_tape_scsi_inquiry(ISTGT_LU_TAPE *spec, CONN_Ptr conn, uint8_t *cdb, uint8_t *data, int alloc_len) { char buf[MAX_TMPBUF]; uint64_t LUI, TPI; uint8_t *cp, *cp2; int hlen = 0, len = 0, plen, plen2; int pc; int pq, pd; int rmb; int evpd; int pg_tag; int i, j; if (alloc_len < 0xff) { return -1; } pq = 0x00; pd = SPC_PERIPHERAL_DEVICE_TYPE_TAPE; rmb = 1; LUI = istgt_get_lui(spec->lu->name, spec->lun & 0xffffU); TPI = istgt_get_lui(spec->lu->name, conn->portal.tag << 16); pc = cdb[2]; evpd = BGET8(&cdb[1], 0); if (evpd) { /* Vital product data */ switch (pc) { case SPC_VPD_SUPPORTED_VPD_PAGES: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; #if 0 data[4] = SPC_VPD_SUPPORTED_VPD_PAGES; /* 0x00 */ data[5] = SPC_VPD_UNIT_SERIAL_NUMBER; /* 0x80 */ data[6] = SPC_VPD_DEVICE_IDENTIFICATION; /* 0x83 */ data[7] = SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES; /* 0x85 */ data[8] = SPC_VPD_EXTENDED_INQUIRY_DATA; /* 0x86 */ data[9] = SPC_VPD_MODE_PAGE_POLICY; /* 0x87 */ data[10]= SPC_VPD_SCSI_PORTS; /* 0x88 */ len = 11 - hlen; /* for DLT8000 */ data[4] = SPC_VPD_SUPPORTED_VPD_PAGES; /* 0x00 */ data[5] = SPC_VPD_UNIT_SERIAL_NUMBER; /* 0x80 */ data[6] = 0xc0; /* Firmware Build Information */ data[7] = 0xc1; /* Subsystem Components Revision */ len = 8 - hlen; #else /* for DLT-S4 */ data[4] = SPC_VPD_SUPPORTED_VPD_PAGES; /* 0x00 */ data[5] = SPC_VPD_UNIT_SERIAL_NUMBER; /* 0x80 */ data[6] = SPC_VPD_DEVICE_IDENTIFICATION; /* 0x83 */ data[7] = 0xb0; /* Sequential-Access Device Capabilities */ data[8] = 0xb1; /* Manufacturer-assigned Serial Number */ data[9] = 0xc0; /* Firmware Build Information */ data[10] = 0xc1; /* Subsystem Components Revision */ len = 11 - hlen; #endif /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_UNIT_SERIAL_NUMBER: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; /* PRODUCT SERIAL NUMBER */ len = strlen(spec->lu->inq_serial); if (len > MAX_LU_SERIAL_STRING) { len = MAX_LU_SERIAL_STRING; } istgt_strcpy_pad(&data[4], len, spec->lu->inq_serial, ' '); /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_DEVICE_IDENTIFICATION: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Identification descriptor 1 */ /* Vendor-Unique Logical Unit Identifier */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_ASCII, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 0, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* T10 VENDOR IDENTIFICATION */ istgt_strcpy_pad(&cp[4], 8, spec->lu->inq_vendor, ' '); /* PRODUCT IDENTIFICATION */ istgt_strcpy_pad(&cp[16], 16, spec->lu->inq_product, ' '); /* PRODUCT SERIAL NUMBER */ istgt_strcpy_pad(&cp[32], 10, spec->lu->inq_serial, ' '); plen = 8 + 16 + 10; cp[3] = plen; len += 4 + plen; /* Identification descriptor 2 */ /* Logical Unit NAA Identifier */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); //BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_FC, 7, 4); //BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_SAS, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_NAA, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* NAA Identifier (WWNN) */ plen = istgt_lu_set_lid(&cp[4], LUI); cp[3] = plen; len += 4 + plen; /* Identification descriptor 3 */ /* Port NAA Identifier */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); //BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_FC, 7, 4); //BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_SAS, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_NAA, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* NAA Identifier (WWPN) */ plen = istgt_lu_set_lid(&cp[4], TPI); cp[3] = plen; len += 4 + plen; /* Identification descriptor 4 */ /* Relative Target Port Identifier */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); //BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_FC, 7, 4); //BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_SAS, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Obsolete */ DSET16(&cp[4], 0); /* Relative Target Port Identifier */ DSET16(&cp[6], 1); /* port1 as port A */ //DSET16(&cp[6], 2); /* port2 as port B */ plen = 4; cp[3] = plen; len += 4 + plen; #undef LU_ISCSI_IDENTIFIER #ifdef LU_ISCSI_IDENTIFIER /* Identification descriptor 1 */ /* Logical Unit */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_NAA, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ #if 0 /* 16bytes ID */ plen = istgt_lu_set_extid(&cp[4], 0, LUI); #else plen = istgt_lu_set_lid(&cp[4], LUI); #endif cp[3] = plen; len += 4 + plen; /* Identification descriptor 2 */ /* T10 VENDOR IDENTIFICATION */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], 0, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 0, 7, 1); /* PIV=0 */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_LOGICAL_UNIT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_T10_VENDOR_ID, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* T10 VENDOR IDENTIFICATION */ istgt_strcpy_pad(&cp[4], 8, spec->lu->inq_vendor, ' '); plen = 8; /* VENDOR SPECIFIC IDENTIFIER */ /* PRODUCT IDENTIFICATION */ istgt_strcpy_pad(&cp[12], 16, spec->lu->inq_product, ' '); /* PRODUCT SERIAL NUMBER */ istgt_strcpy_pad(&cp[28], MAX_LU_SERIAL_STRING, spec->lu->inq_serial, ' '); plen += 16 + MAX_LU_SERIAL_STRING; cp[3] = plen; len += 4 + plen; /* Identification descriptor 3 */ /* Target Device */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_DEVICE, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp[4], MAX_TARGET_NAME, "%s", spec->lu->name); cp[3] = plen; len += 4 + plen; /* Identification descriptor 4 */ /* Target Port */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp[4], MAX_TARGET_NAME, "%s"",t,0x""%4.4x", spec->lu->name, conn->portal.tag); cp[3] = plen; len += 4 + plen; /* Identification descriptor 5 */ /* Relative Target Port */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_RELATIVE_TARGET_PORT, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Obsolete */ DSET16(&cp[4], 0); /* Relative Target Port Identifier */ //DSET16(&cp[6], 1); /* port1 as port A */ //DSET16(&cp[6], 2); /* port2 as port B */ DSET16(&cp[6], (uint16_t) (1 + conn->portal.idx)); plen = 4; cp[3] = plen; len += 4 + plen; /* Identification descriptor 6 */ /* Target port group */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_TARGET_PORT_GROUP, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Reserved */ DSET16(&cp[4], 0); /* TARGET PORT GROUP */ DSET16(&cp[6], (uint16_t) (conn->portal.tag)); plen = 4; cp[3] = plen; len += 4 + plen; /* Identification descriptor 7 */ /* Logical unit group */ cp = &data[hlen + len]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp[0], SPC_VPD_CODE_SET_BINARY, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp[1], 1, 7, 1); /* PIV */ BDADD8W(&cp[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp[1], SPC_VPD_IDENTIFIER_TYPE_LOGICAL_UNIT_GROUP, 3, 4); /* Reserved */ cp[2] = 0; /* IDENTIFIER LENGTH */ cp[3] = 0; /* IDENTIFIER */ /* Reserved */ DSET16(&cp[4], 0); /* LOGICAL UNIT GROUP */ DSET16(&cp[6], (uint16_t) (spec->lu->num)); plen = 4; cp[3] = plen; len += 4 + plen; #endif /* LU_ISCSI_IDENTIFIER */ /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_EXTENDED_INQUIRY_DATA: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; /* RTO(3) GRD_CHK(2) APP_CHK(1) REF_CHK(0) */ data[4] = 0; /* GROUP_SUP(4) PRIOR_SUP(3) HEADSUP(2) ORDSUP(1) SIMPSUP(0) */ data[5] = 0; /* NV_SUP(1) V_SUP(0) */ data[6] = 0; /* Reserved[7-63] */ memset(&data[7], 0, (64 - 7)); len = 64 - hlen; /* PAGE LENGTH */ data[3] = len; break; case SPC_VPD_MANAGEMENT_NETWORK_ADDRESSES: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; #if 0 /* Network services descriptor N */ cp = &data[hlen + len]; /* ASSOCIATION(6-5) SERVICE TYPE(4-0) */ BDSET8W(&cp[0], 0x00, 6, 2); BDADD8W(&cp[0], 0x00, 4, 5); /* Reserved */ cp[1] = 0; /* NETWORK ADDRESS LENGTH */ DSET16(&cp[2], 0); /* NETWORK ADDRESS */ cp[4] = 0; /* ... */ plen = 0; DSET16(&cp[2], plen); len += 4 + plen; #endif /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_MODE_PAGE_POLICY: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Mode page policy descriptor 1 */ cp = &data[hlen + len]; /* POLICY PAGE CODE(5-0) */ BDSET8W(&cp[0], 0x3f, 5, 6); /* all page code */ /* POLICY SUBPAGE CODE */ cp[1] = 0xff; /* all sub page */ /* MLUS(7) MODE PAGE POLICY(1-0) */ //BDSET8(&cp[2], 1, 7); /* multiple logical units share */ BDSET8(&cp[2], 0, 7); /* own copy */ BDADD8W(&cp[2], 0x00, 1, 2); /* Shared */ //BDADD8W(&cp[2], 0x01, 1, 2); /* Per target port */ //BDADD8W(&cp[2], 0x02, 1, 2); /* Per initiator port */ //BDADD8W(&cp[2], 0x03, 1, 2); /* Per I_T nexus */ /* Reserved */ cp[3] = 0; len += 4; /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; case SPC_VPD_SCSI_PORTS: /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* PAGE LENGTH */ DSET16(&data[2], 0); hlen = 4; /* Identification descriptor list */ for (i = 0; i < spec->lu->maxmap; i++) { pg_tag = spec->lu->map[i].pg_tag; /* skip same pg_tag */ for (j = 0; j < i; j++) { if (spec->lu->map[j].pg_tag == pg_tag) { goto skip_pg_tag; } } /* Identification descriptor N */ cp = &data[hlen + len]; /* Reserved */ DSET16(&cp[0], 0); /* RELATIVE PORT IDENTIFIER */ DSET16(&cp[2], (uint16_t) (1 + pg_tag)); /* Reserved */ DSET16(&cp[4], 0); /* INITIATOR PORT TRANSPORTID LENGTH */ DSET16(&cp[6], 0); /* Reserved */ DSET16(&cp[8], 0); /* TARGET PORT DESCRIPTORS LENGTH */ DSET16(&cp[10], 0); len += 12; plen2 = 0; /* Target port descriptor 1 */ cp2 = &data[hlen + len + plen2]; /* PROTOCOL IDENTIFIER(7-4) CODE SET(3-0) */ BDSET8W(&cp2[0], SPC_PROTOCOL_IDENTIFIER_ISCSI, 7, 4); BDADD8W(&cp2[0], SPC_VPD_CODE_SET_UTF8, 3, 4); /* PIV(7) ASSOCIATION(5-4) IDENTIFIER TYPE(3-0) */ BDSET8W(&cp2[1], 1, 7, 1); /* PIV */ BDADD8W(&cp2[1], SPC_VPD_ASSOCIATION_TARGET_PORT, 5, 2); BDADD8W(&cp2[1], SPC_VPD_IDENTIFIER_TYPE_SCSI_NAME, 3, 4); /* Reserved */ cp2[2] = 0; /* IDENTIFIER LENGTH */ cp2[3] = 0; /* IDENTIFIER */ plen = snprintf((char *) &cp2[4], MAX_TARGET_NAME, "%s"",t,0x""%4.4x", spec->lu->name, pg_tag); cp2[3] = plen; plen2 += 4 + plen; /* TARGET PORT DESCRIPTORS LENGTH */ DSET16(&cp[10], plen2); len += plen2; skip_pg_tag: ; } /* PAGE LENGTH */ if (len > 0xffff) { len = 0xffff; } DSET16(&data[2], len); break; /* for DLT-S4 */ case 0xb0: /* Sequential-Access Device Capabilities */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; len = 0x4; memset(&data[4], 0, len); //BSET8(&data[4], 0); /* WORM */ /* PAGE LENGTH */ data[3] = len; break; case 0xb1: /* Manufacturer-assigned Serial Number */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; len = 0x10; memset(&data[4], 0, len); /* Manufacturer Serial Number */ snprintf(buf, sizeof buf, "%16.16d", 0); istgt_strcpy_pad(&data[4], 16, buf, ' '); /* PAGE LENGTH */ data[3] = len; break; /* for DLT8000 */ case 0xc0: /* Firmware Build Information */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; len = 0x20; memset(&data[4], 0, len); /* PAGE LENGTH */ data[3] = len; break; case 0xc1: /* Subsystem Components Revision */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* PAGE CODE */ data[1] = pc; /* Reserved */ data[2] = 0; /* PAGE LENGTH */ data[3] = 0; hlen = 4; len = 0x14; memset(&data[4], 0, len); /* Media Loader Present Flag */ data[18] = 0; /* Library Present Flag */ data[19] = 0; /* PAGE LENGTH */ data[3] = len; break; default: if (pc >= 0xc0 && pc <= 0xff) { ISTGT_WARNLOG("Vendor specific INQUIRY VPD page 0x%x\n", pc); } else { ISTGT_ERRLOG("unsupported INQUIRY VPD page 0x%x\n", pc); } return -1; } } else { /* Standard INQUIRY data */ /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], pq, 7, 3); BDADD8W(&data[0], pd, 4, 5); /* RMB(7) */ BDSET8W(&data[1], rmb, 7, 1); /* VERSION */ /* See SPC3/SBC2/MMC4/SAM2 for more details */ data[2] = SPC_VERSION_SPC3; /* NORMACA(5) HISUP(4) RESPONSE DATA FORMAT(3-0) */ BDSET8W(&data[3], 2, 3, 4); /* format 2 */ /* ADDITIONAL LENGTH */ data[4] = 0; hlen = 5; /* SCCS(7) ACC(6) TPGS(5-4) 3PC(3) PROTECT(0) */ data[5] = 0; /* BQUE(7) ENCSERV(6) VS(5) MULTIP(4) MCHNGR(3) ADDR16(0) */ data[6] = 0; /* WBUS16(5) SYNC(4) LINKED(3) CMDQUE(1) VS(0) */ data[7] = 0; /* T10 VENDOR IDENTIFICATION */ istgt_strcpy_pad(&data[8], 8, spec->lu->inq_vendor, ' '); /* PRODUCT IDENTIFICATION */ istgt_strcpy_pad(&data[16], 16, spec->lu->inq_product, ' '); /* PRODUCT REVISION LEVEL */ istgt_strcpy_pad(&data[32], 4, spec->lu->inq_revision, ' '); /* Vendor specific */ memset(&data[36], 0x20, 20); #if 1 /* for Quantum DLT */ /* Product Family(7-4) Released Firmware(3-0) */ //BDSET8W(&data[36], 5, 7, 4); /* 20/40GB */ BDSET8W(&data[36], 11, 7, 4); /* 40/80GB */ BDSET8W(&data[36], 1, 3, 4); /* Vxxx firmware */ /* Firmware Major Version # */ data[37] = 0x01; /* Firmware Minor Version # */ data[38] = 0x00; /* EEPROM Format Major Version # */ data[39] = 0x01; /* EEPROM Format Minor Version # */ data[40] = 0x00; /* Firmware Personality */ data[41] = 0x04; /* OEM family */ /* Firmware Sub-Personality */ data[42] = 0x01; /* primary firmware personality variant */ /* Vendor Unique Subtype */ data[43] = 0x00; /* Controller Hardware Version # */ data[44] = 0x01; /* Drive EEPROM Version # */ data[45] = 0x01; /* Drive Hardware Version # */ data[46] = 0x01; /* Media Loader Firmware Version # */ data[47] = 0x00; /* Media Loader Hardware Version # */ data[48] = 0x00; /* Media Loader Mechanical Version # */ data[49]= 0x00; /* Media Loader Present Flag */ data[50] = 0; /* Library Present Flag */ data[51] = 0; /* Module Revision */ istgt_strcpy_pad(&data[54], 4, TAPE_MODULE_REV, ' '); #endif /* CLOCKING(3-2) QAS(1) IUS(0) */ data[56] = 0; /* Reserved */ data[57] = 0; /* VERSION DESCRIPTOR 1-8 */ DSET16(&data[58], 0x0960); /* iSCSI (no version claimed) */ DSET16(&data[60], 0x0300); /* SPC-3 (no version claimed) */ DSET16(&data[62], 0x0360); /* SSC-2 (no version claimed) */ DSET16(&data[64], 0x0040); /* SAM-2 (no version claimed) */ DSET16(&data[66], 0x0000); DSET16(&data[68], 0x0000); DSET16(&data[70], 0x0000); DSET16(&data[72], 0x0000); /* Reserved[74-95] */ memset(&data[74], 0, (96 - 74)); /* Vendor specific parameters[96-n] */ //data[96] = 0; len = 96 - hlen; /* ADDITIONAL LENGTH */ data[4] = len; } return hlen + len; } #define MODE_SENSE_PAGE_INIT(B,L,P,SP) \ do { \ memset((B), 0, (L)); \ if ((SP) != 0x00) { \ (B)[0] = (P) | 0x40; /* PAGE + SPF=1 */ \ (B)[1] = (SP); \ DSET16(&(B)[2], (L) - 4); \ } else { \ (B)[0] = (P); \ (B)[1] = (L) - 2; \ } \ } while (0) static int istgt_lu_tape_scsi_mode_sense_page(ISTGT_LU_TAPE *spec, CONN_Ptr conn, uint8_t *cdb, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int len = 0; int plen; int i; #if 0 printf("SENSE pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage); #endif ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE: pc=%d, page=%2.2x, subpage=%2.2x\n", pc, page, subpage); if (pc == 0x00) { /* Current values */ } else if (pc == 0x01) { /* Changeable values */ if (page != 0x08) { /* not supported */ return 0; } } else if (pc == 0x02) { /* Default values */ } else { /* Saved values */ } cp = &data[len]; switch (page) { case 0x00: /* Vendor specific */ break; case 0x01: /* Read-Write Error Recovery */ break; case 0x02: /* Disconnect-Reconnect mode page */ break; case 0x03: case 0x04: case 0x05: case 0x06: case 0x07: case 0x08: /* Reserved */ break; case 0x09: /* Obsolete */ break; case 0x0a: /* Control mode page */ break; case 0x0b: case 0x0c: case 0x0d: case 0x0e: /* Reserved */ break; case 0x0f: /* Data Compression */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Data Compression\n"); if (subpage != 0x00) break; plen = 0x0e + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); if (spec->compression) { BDADD8(&cp[2], 1, 7); /* DCE=1 compression enable */ } else { BDADD8(&cp[2], 0, 7); /* DCE=0 compression disable */ } //BDADD8(&cp[2], 0, 6); /* DCC=0 not support compression */ BDADD8(&cp[2], 1, 6); /* DCC=1 support compression */ BDADD8(&cp[3], 1, 7); /* DDE=1 decompression enable */ BDADD8W(&cp[3], 0, 6, 2); /* RED=0 not support */ /* COMPRESSION ALGORITHM */ //DSET32(&cp[4], 0); //DSET32(&cp[4], 0x03); /* IBM ALDC with 512 byte buffer */ //DSET32(&cp[4], 0x04); /* IBM ALDC with 1024 byte buffer */ //DSET32(&cp[4], 0x05); /* IBM ALDC with 2048 byte buffer */ //DSET32(&cp[4], 0x10); /* IBM IDRC */ DSET32(&cp[4], TAPE_COMP_ALGORITHM); /* DECOMPRESSION ALGORITHM */ //DSET32(&cp[8], 0); DSET32(&cp[8], TAPE_COMP_ALGORITHM); len += plen; break; case 0x10: /* Device Configuration */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Device Configuration\n"); if (subpage != 0x00) break; plen = 0x0e + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); /* WRITE DELAY TIME */ DSET16(&cp[6], TAPE_WRITE_DELAY); /* RSMK(5) */ BDADD8(&data[8], 0, 5); /* report setmarks not support */ /* LOIS(6) */ BDADD8(&data[8], 1, 6); /* EEG(4) SEW(3) */ BDADD8(&data[10], 1, 4); BDADD8(&data[10], 1, 3); /* SELECT DATA COMPRESSION ALGORITHM */ if (spec->compression) { data[14] = 1; /* data compression is enabled */ } len += plen; break; case 0x11: /* Medium Partition */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Medium Partition\n"); if (subpage != 0x00) break; plen = 0x08 + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x12: /* Obsolete */ break; case 0x13: /* Obsolete */ break; case 0x14: /* Obsolete */ break; case 0x15: case 0x16: case 0x17: /* Reserved */ break; case 0x18: /* Protocol Specific LUN */ break; case 0x19: /* Protocol Specific Port */ break; case 0x1a: /* Power Condition */ break; case 0x1b: /* Reserved */ break; case 0x1c: /* Informational Exceptions Control */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SENSE Informational Exceptions Control\n"); if (subpage != 0x00) break; plen = 0x0a + 2; MODE_SENSE_PAGE_INIT(cp, plen, page, subpage); len += plen; break; case 0x1d: case 0x1e: case 0x1f: /* Reserved */ break; case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x26: case 0x27: case 0x28: case 0x29: case 0x2a: case 0x2b: case 0x2c: case 0x2d: case 0x2e: case 0x2f: case 0x30: case 0x31: case 0x32: case 0x33: case 0x34: case 0x35: case 0x36: case 0x37: case 0x38: case 0x39: case 0x3a: case 0x3b: case 0x3c: case 0x3d: case 0x3e: /* Vendor-specific */ break; case 0x3f: switch (subpage) { case 0x00: /* All mode pages */ for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_tape_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len); } break; case 0xff: /* All mode pages and subpages */ for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_tape_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0x00, &cp[len], alloc_len); } for (i = 0x00; i < 0x3e; i ++) { len += istgt_lu_tape_scsi_mode_sense_page(spec, conn, cdb, pc, i, 0xff, &cp[len], alloc_len); } break; default: /* 0x01-0x3e: Reserved */ break; } } return len; } static int istgt_lu_tape_scsi_mode_sense6(ISTGT_LU_TAPE *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int hlen = 0, len = 0, plen; int total; int llbaa = 0; data[0] = 0; /* Mode Data Length */ if (spec->mload) { //data[1] = 0; /* Medium Type (no media) */ //data[1] = TAPE_MEDIATYPE_LTO; /* Medium Type (LTO) */ data[1] = MEDIATYPE_DFLT; /* Medium Type */ data[2] = 0; /* Device-Specific Parameter */ if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { BDADD8(&data[2], 1, 7); /* WP */ } } else { data[1] = 0; /* Medium Type (no media) */ data[2] = 0; /* Device-Specific Parameter */ } BDADD8W(&data[2], 1, 6, 3); /* Buffed Mode=1 */ data[3] = 0; /* Block Descripter Length */ hlen = 4; cp = &data[4]; if (dbd) { /* Disable Block Descripters */ len = 0; } else { if (llbaa) { if (spec->mload) { /* Number of Blocks */ DSET64(&cp[0], 0ULL); /* all of the remaining */ /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], (uint32_t) spec->lblen); } else { /* Number of Blocks */ DSET64(&cp[0], 0ULL); /* all of the remaining */ /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], 0); } len = 16; } else { if (spec->mload) { /* Number of Blocks */ DSET32(&cp[0], 0); /* all of the remaining */ /* Block Length */ DSET32(&cp[4], (uint32_t) spec->lblen); cp[0] = DENSITY_DFLT; /* Density Code */ cp[4] = 0; /* Reserved */ } else { /* Number of Blocks */ DSET32(&cp[0], 0); /* all of the remaining */ /* Block Length */ DSET32(&cp[4], 0); cp[0] = 0; /* Density Code */ cp[4] = 0; /* Reserved */ } len = 8; } cp += len; } data[3] = len; /* Block Descripter Length */ plen = istgt_lu_tape_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len); cp += plen; total = hlen + len + plen; data[0] = total - 1; /* Mode Data Length */ return total; } static int istgt_lu_tape_scsi_mode_sense10(ISTGT_LU_TAPE *spec, CONN_Ptr conn, uint8_t *cdb, int dbd, int llbaa, int pc, int page, int subpage, uint8_t *data, int alloc_len) { uint8_t *cp; int hlen = 0, len = 0, plen; int total; DSET16(&data[0], 0); /* Mode Data Length */ if (spec->mload) { //data[2] = 0; /* Medium Type (no media) */ //data[2] = TAPE_MEDIATYPE_LTO; /* Medium Type (DLT) */ data[2] = MEDIATYPE_DFLT; /* Medium Type */ data[3] = 0; /* Device-Specific Parameter */ if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { BDADD8(&data[3], 1, 7); /* WP */ } } else { data[2] = 0; /* Medium Type (no media) */ data[3] = 0; /* Device-Specific Parameter */ } BDADD8W(&data[3], 1, 6, 3); /* Buffed Mode=1 */ if (llbaa) { BDSET8(&data[4], 1, 1); /* Long LBA */ } else { BDSET8(&data[4], 0, 1); /* Short LBA */ } data[5] = 0; /* Reserved */ DSET16(&data[6], 0); /* Block Descripter Length */ hlen = 8; cp = &data[8]; if (dbd) { /* Disable Block Descripters */ len = 0; } else { if (llbaa) { if (spec->mload) { /* Number of Blocks */ DSET64(&cp[0], 0ULL); /* all of the remaining */ /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], (uint32_t) spec->lblen); } else { /* Number of Blocks */ DSET64(&cp[0], 0ULL); /* all of the remaining */ /* Reserved */ DSET32(&cp[8], 0); /* Block Length */ DSET32(&cp[12], 0); } len = 16; } else { if (spec->mload) { /* Number of Blocks */ DSET32(&cp[0], 0); /* all of the remaining */ /* Block Length */ DSET32(&cp[4], (uint32_t) spec->lblen); cp[0] = DENSITY_DFLT; /* Density Code */ cp[4] = 0; /* Reserved */ } else { /* Number of Blocks */ DSET32(&cp[0], 0); /* all of the remaining */ /* Block Length */ DSET32(&cp[4], 0); cp[0] = 0; /* Density Code */ cp[4] = 0; /* Reserved */ } len = 8; } cp += len; } DSET16(&data[6], len); /* Block Descripter Length */ plen = istgt_lu_tape_scsi_mode_sense_page(spec, conn, cdb, pc, page, subpage, &cp[0], alloc_len); cp += plen; total = hlen + len + plen; DSET16(&data[0], total - 2); /* Mode Data Length */ return total; } static int istgt_lu_tape_transfer_data(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *buf, size_t bufsize, size_t len) { int rc; if (len > bufsize) { ISTGT_ERRLOG("bufsize(%d) too small\n", bufsize); return -1; } rc = istgt_iscsi_transfer_out(conn, lu_cmd, buf, bufsize, len); if (rc < 0) { ISTGT_ERRLOG("iscsi_transfer_out()\n"); return -1; } return 0; } static int istgt_lu_tape_scsi_mode_select_page(ISTGT_LU_TAPE *spec, CONN_Ptr conn, uint8_t *cdb, int pf, int sp, uint8_t *data, size_t len) { int ps, spf, page, subpage; int hlen, plen; int rc; if (pf == 0) { /* vendor specific */ return 0; } if (len < 1) return 0; ps = BGET8(&data[0], 7); spf = BGET8(&data[0], 6); page = data[0] & 0x3f; if (spf) { /* Sub_page mode page format */ hlen = 4; if (len < hlen) return 0; subpage = data[1]; plen = DGET16(&data[2]); } else { /* Page_0 mode page format */ hlen = 2; if (len < hlen) return 0; subpage = 0; plen = data[1]; } plen += hlen; if (len < plen) return 0; #if 0 printf("SELECT ps=%d, page=%2.2x, subpage=%2.2x\n", ps, page, subpage); #endif ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT: ps=%d, page=%2.2x, subpage=%2.2x\n", ps, page, subpage); switch (page) { case 0x0f: /* Data Compression */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Data Compression\n"); { int dce, dde, red; uint32_t compalgo, decompalgo; if (subpage != 0x00) break; if (plen != 0x0e + hlen) { /* unknown format */ break; } dce = BGET8(&data[2], 7); /* DCE */ dde = BGET8(&data[3], 7); /* DDE */ red = BGET8W(&data[3], 6, 2); /* RED */ compalgo = DGET32(&data[4]); decompalgo = DGET32(&data[8]); switch (compalgo) { case 0x00: /* default by hard */ compalgo = TAPE_COMP_ALGORITHM; case 0x03: /* ALDC 512 */ case 0x04: /* ALDC 1024 */ case 0x05: /* ALDC 2048 */ case 0x10: /* IDRC */ spec->compalgo = compalgo; spec->vtcompalgo = MARK_COMPALGO_NONE; break; default: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "unsupported Compression Algorithm\n"); /* force to default */ spec->compalgo = TAPE_COMP_ALGORITHM; spec->vtcompalgo = MARK_COMPALGO_NONE; break; } if (dce) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Data compression enable\n"); spec->compression = 1; } else { spec->compression = 0; } if (dde) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Data decompression enable\n"); } break; } case 0x10: /* Device Configuration */ ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "MODE_SELECT Device Configuration\n"); { if (subpage != 0x00) break; if (plen != 0x0e + hlen) { /* unknown format */ break; } break; } default: /* not supported */ break; } len -= plen; if (len != 0) { rc = istgt_lu_tape_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[plen], len); if (rc < 0) { return rc; } } return 0; } static int istgt_convert_signed_24bits(uint32_t usval) { int value; /* 24bits two's complement notation */ if (usval > 0x007fffff) { usval -= 1; usval = ~usval; usval &= 0x00ffffff; value = (int) usval; value = -value; } else { value = (int) usval; } return value; } #define THREAD_YIELD do { istgt_yield(); usleep(1000); } while (0) static int istgt_lu_tape_shrink_media(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t request_len, uint8_t *data) { struct stat st; uint64_t mediasize; uint64_t marklen; uint32_t mediaflags; int fd; fd = spec->fd; mediasize = spec->size; mediaflags = spec->mflags; marklen = spec->ctlblock->marklen; if (fstat(fd, &st) == -1) { ISTGT_ERRLOG("fstat() failed\n"); return -1; } if (S_ISREG(st.st_mode)) { /* media is file */ if (mediaflags & ISTGT_LU_FLAG_MEDIA_DYNAMIC) { if (request_len < ISTGT_LU_MEDIA_SIZE_MIN) { request_len = ISTGT_LU_MEDIA_SIZE_MIN; } mediasize = request_len; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Shrink: %" PRIu64 " -> %" PRIu64 "\n", st.st_size, request_len); #endif /* TAPE_DEBUG */ /* truncate */ if (ftruncate(fd, request_len) == -1) { ISTGT_ERRLOG("ftruncate() failed\n"); return -1; } fsync(fd); spec->size = mediasize; } } else { /* media is not file */ } return 0; } static int istgt_lu_tape_scsi_erase(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint8_t *data) { struct stat st; uint64_t mediasize; uint64_t ctlblocklen; uint64_t marklen; uint64_t request_len; uint32_t mediaflags; int data_len; int newfile; int fd; fd = spec->fd; mediasize = spec->size; mediaflags = spec->mflags; ctlblocklen = spec->ctlblock->ctlblocklen; marklen = spec->ctlblock->marklen; if (ctlblocklen < CTLBLOCKLEN) { ctlblocklen = CTLBLOCKLEN; } if (marklen < MARK_LENGTH) { marklen = MARK_LENGTH; } if (spec->lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { /* WRITE PROTECTED */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_DATA_PROTECT, 0x27, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (!spec->bot) { /* PARAMETER VALUE INVALID */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_ILLEGAL_REQUEST, 0x26, 0x02); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (spec->lu->lun[spec->lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { /* INTERNAL TARGET FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x44, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* low I/O */ if (fstat(fd, &st) == -1) { ISTGT_ERRLOG("fstat() failed\n"); io_failure: /* LOGICAL UNIT FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x3e, 0x01); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* clear ctlblock + BOT + EOD */ request_len = ctlblocklen + marklen * 2; spec->ctlblock->marks[1].offset = MARK_END; spec->ctlblock->marks[1].lbpos = MARK_END; spec->ctlblock->marks[1].prev = 0ULL; memset(data, 0, request_len); if (istgt_lu_tape_seek(spec, 0) == -1) { ISTGT_ERRLOG("lu_tape_lseek() failed\n"); goto io_failure; } if (istgt_lu_tape_write(spec, data, request_len) != request_len) { ISTGT_ERRLOG("lu_tape_write() failed\n"); goto io_failure; } fsync(fd); /* initialize filemarks */ newfile = 1; if (istgt_lu_tape_init_ctlblock(spec, newfile) < 0) { ISTGT_ERRLOG("lu_tape_init_ctlblock() failed\n"); goto io_failure; } fsync(fd); if (S_ISREG(st.st_mode)) { /* media is file */ /* truncate and extend */ if (ftruncate(fd, request_len) == -1) { ISTGT_ERRLOG("ftruncate() failed\n"); goto io_failure; } fsync(fd); if (mediaflags & ISTGT_LU_FLAG_MEDIA_DYNAMIC) { if (request_len < ISTGT_LU_MEDIA_SIZE_MIN) { request_len = ISTGT_LU_MEDIA_SIZE_MIN; } mediasize = request_len; } memset(data, 0, marklen); if (istgt_lu_tape_seek(spec, (mediasize - marklen)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); goto io_failure; } if (istgt_lu_tape_write(spec, data, marklen) != marklen) { ISTGT_ERRLOG("istgt_lu_tape_write() failed\n"); goto io_failure; } fsync(fd); spec->size = mediasize; } else { /* media is not file */ uint64_t offset, wlen, rest; /* clear with 256K */ offset = request_len; wlen = 256*1024; memset(data, 0, wlen); for ( ; offset < mediasize - wlen; offset += wlen) { THREAD_YIELD; if (istgt_lu_tape_write(spec, data, wlen) != wlen) { ISTGT_ERRLOG("lu_tape_write() failed\n"); goto io_failure; } } /* clear rest size */ rest = mediasize % wlen; if (rest != 0) { THREAD_YIELD; if (istgt_lu_tape_write(spec, data, rest) != rest) { ISTGT_ERRLOG("lu_tape_write() failed\n"); goto io_failure; } } THREAD_YIELD; fsync(fd); } /* rewind */ istgt_lu_tape_rewind(spec); /* complete erase */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_valid_mark_magic(tape_markblock_t *mbp) { if (mbp == NULL) return 0; if (memcmp(mbp->magic, MARK_BOTMAGIC, MARK_MAGICLEN) == 0) return 1; if (memcmp(mbp->magic, MARK_EOTMAGIC, MARK_MAGICLEN) == 0) return 1; if (memcmp(mbp->magic, MARK_EOFMAGIC, MARK_MAGICLEN) == 0) return 1; if (memcmp(mbp->magic, MARK_EODMAGIC, MARK_MAGICLEN) == 0) return 1; if (memcmp(mbp->magic, MARK_DATAMAGIC, MARK_MAGICLEN) == 0) return 1; return 0; } static int istgt_lu_tape_search_lbpos(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lbpos, uint8_t *data) { tape_markblock_t *mbp; uint64_t tape_leader; uint64_t marklen, alignment, padlen; uint64_t lbpos1, offset1, lbpos2, offset2; uint64_t offset, prev; int found_lbpos = 0; int data_len; int index_i; int rc; int i; tape_leader = spec->ctlblock->ctlblocklen; marklen = spec->ctlblock->marklen; alignment = spec->ctlblock->alignment; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "search lbpos=%" PRIu64 "\n", lbpos); #endif /* TAPE_DEBUG */ /* firset step, jump near position by EOF */ index_i = -1; for (i = 0; i < MAX_FILEMARKS - 1; i++) { offset1 = spec->ctlblock->marks[i].offset; offset2 = spec->ctlblock->marks[i + 1].offset; lbpos1 = spec->ctlblock->marks[i].lbpos; lbpos2 = spec->ctlblock->marks[i + 1].lbpos; if (offset1 == MARK_END) { /* no more marks */ break; } if (offset2 == MARK_END) { /* adjust to real media size */ offset2 = spec->size; } /* lbpos within EOFs? */ if (lbpos >= lbpos1 && lbpos < lbpos2) { index_i = i; break; } } if (index_i < 0) { /* END-OF-PARTITION/MEDIUM DETECTED */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_MEDIUM_ERROR, 0x00, 0x02); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } /* next step, search in file */ mbp = (tape_markblock_t *) data; prev = spec->ctlblock->marks[index_i].prev; found_lbpos = 0; for (offset = offset1; offset < offset2; ) { if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); break; } rc = istgt_lu_tape_read_native_mark(spec, mbp); if (rc < 0) { ISTGT_ERRLOG("lu_tape_read_native_mark() failed: rc %d\n", rc); break; } /* check in logical block */ if (!istgt_lu_tape_valid_mark_magic(mbp)) { ISTGT_ERRLOG("bad magic offset %" PRIu64 "\n", offset); break; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "read mlbpos=%" PRIu64 ", mlblen=%" PRIu64 ", moffset=%" PRIu64 ", offset=%" PRIu64 ", index=%d\n", mbp->lbpos, mbp->lblen, mbp->offset, offset, index_i); #endif /* TAPE_DEBUG */ if (lbpos == mbp->lbpos) { found_lbpos = 1; offset = mbp->offset; break; } /* next offset to read */ prev = offset; offset += marklen + mbp->lblen; if (offset % alignment) { padlen = alignment; padlen -= offset % alignment; offset += padlen; } } if (!found_lbpos) { /* within EOFs, but not found */ /* INTERNAL TARGET FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x44, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "set lbpos=%" PRIu64 ", offset=%" PRIu64 ", index=%d\n", lbpos, offset, index_i); #endif /* TAPE_DEBUG */ /* update information */ spec->index = index_i; spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; spec->bot = spec->eof = spec->eod = spec->eom = 0; if (index_i == 0 && offset == 0) { spec->bot = 1; } else if (offset == spec->ctlblock->marks[index_i].offset) { if (offset == MARK_END) { spec->eom = 1; } else { spec->eof = 1; } } /* complete search, new position to lbpos */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_search_lbpos_fast_reverse(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lbpos, int count, uint8_t *data) { uint64_t xlbpos, offset, prev; int index_i; xlbpos = spec->lbpos; offset = spec->offset; prev = spec->prev; index_i = spec->index; /* now only support -1 */ if (count != -1) return -1; /* END mark is special */ if (offset == MARK_END || spec->ctlblock->marks[index_i].offset == MARK_END || spec->ctlblock->marks[index_i + 1].offset == MARK_END) return -1; /* this lbpos have previous offset? */ if (lbpos != xlbpos) return -1; if (offset == spec->ctlblock->marks[index_i + 1].offset && spec->ctlblock->marks[index_i + 1].prev != 0ULL) { /* get from EOF mark */ offset = spec->ctlblock->marks[index_i + 1].prev; lbpos = spec->ctlblock->marks[index_i + 1].lbpos; lbpos--; prev = 0ULL; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "set lbpos=%" PRIu64 ", offset=%" PRIu64 ", index=%d\n", lbpos, offset, index_i); #endif /* TAPE_DEBUG */ /* update information */ spec->index = index_i; spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; spec->bot = spec->eof = spec->eod = spec->eom = 0; if (index_i == 0 && offset == 0) { spec->bot = 1; } else if (offset == spec->ctlblock->marks[index_i].offset) { if (offset == MARK_END) { spec->eom = 1; } else { spec->eof = 1; } } /* complete search, new position to lbpos */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } /* no method for fast reverse */ return -1; } static int istgt_lu_tape_scsi_space(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, int code, int count, uint8_t *data) { uint64_t lbpos, offset, prev; int found_bot = 0, found_eom = 0; int data_len; int index_i; int i; if (code != 0x03 && count == 0) { /* no-op except EOD */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } lbpos = spec->lbpos; offset = spec->offset; prev = spec->prev; index_i = spec->index; if (code == 0x00) { /* Logical blocks */ if (count < 0) { /* reverse */ /* first check search cache etc. */ data_len = istgt_lu_tape_search_lbpos_fast_reverse(spec, conn, lu_cmd, lbpos, count, data); if (data_len > 0) { /* scsi condition met */ lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return data_len; } else if (data_len == 0) { /* found position */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } count = -count; if (lbpos < (uint64_t) count) { lbpos = 0ULL; } else { lbpos -= (uint64_t) count; } } else if (count > 0) { /* forward */ if ((uint64_t) count > LBPOS_MAX - lbpos) { lbpos = LBPOS_MAX; } else { lbpos += (uint64_t) count; } } /* search in file (logical blocks) */ data_len = istgt_lu_tape_search_lbpos(spec, conn, lu_cmd, lbpos, data); if (data_len != 0) { /* sense data build by function */ lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return data_len; } } else if (code == 0x01) { /* Filemarks */ if (count < 0) { /* reverse */ for (i = 0; i > count; i--) { if (index_i + i == 0) { found_bot = 1; break; } } index_i += i; offset = spec->ctlblock->marks[index_i].offset; if (offset == MARK_END) { /* INTERNAL TARGET FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x44, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return data_len; } /* position to EOF */ lbpos = spec->ctlblock->marks[index_i + 1].lbpos; offset = spec->ctlblock->marks[index_i + 1].offset; prev = spec->ctlblock->marks[index_i + 1].prev; } else if (count > 0) { /* forward */ for (i = 0; i < count; i++) { if (spec->ctlblock->marks[index_i + i].offset == MARK_END) { found_eom = 1; break; } } index_i += i; offset = spec->ctlblock->marks[index_i].offset; if (found_eom || offset == MARK_END) { /* END-OF-DATA DETECTED */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_BLANK_CHECK, 0x00, 0x05); DSET32(&data[2+3], (uint32_t) count - i); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return data_len; } lbpos = spec->ctlblock->marks[index_i].lbpos; /* position to next block of EOF */ prev = offset; offset += spec->ctlblock->marklen; lbpos++; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "set lbpos=%" PRIu64 ", offset=%" PRIu64 ", index=%d\n", lbpos, offset, index_i); #endif /* TAPE_DEBUG */ /* update information */ spec->index = index_i; spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; spec->bot = spec->eof = spec->eod = spec->eom = 0; if (index_i == 0 && offset == 0) { spec->bot = 1; } else if (offset == spec->ctlblock->marks[index_i].offset) { if (offset == MARK_END) { spec->eom = 1; } else { spec->eof = 1; } } } else if (code == 0x03) { /* End-of-data */ index_i = -1; for (i = 0; i < MAX_FILEMARKS ; i++) { if (spec->ctlblock->marks[i].offset == MARK_END) { index_i = i; break; } } if (index_i <= 0) { /* INTERNAL TARGET FAILURE */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_HARDWARE_ERROR, 0x44, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return data_len; } /* skip EOT (position to last EOF) */ index_i--; lbpos = spec->ctlblock->marks[index_i].lbpos; offset = spec->ctlblock->marks[index_i].offset; /* position to next block of EOF */ prev = offset; offset += spec->ctlblock->marklen; lbpos++; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "set lbpos=%" PRIu64 ", offset=%" PRIu64 ", index=%d\n", lbpos, offset, index_i); #endif /* TAPE_DEBUG */ /* update information */ spec->index = index_i; spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; spec->bot = spec->eof = spec->eod = spec->eom = 0; if (index_i == 0 && offset == 0) { spec->bot = 1; } else if (offset == spec->ctlblock->marks[index_i].offset) { if (offset == MARK_END) { spec->eom = 1; } else { spec->eof = 1; } } } else { /* INVALID FIELD IN CDB */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return data_len; } /* complete space command, new position to lbpos */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_scsi_locate(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint32_t loi, uint8_t *data) { uint64_t lbpos; int data_len; if (loi == 0) { /* position to zero (BOT) */ istgt_lu_tape_rewind(spec); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } lbpos = (uint64_t) loi; /* search logical block */ data_len = istgt_lu_tape_search_lbpos(spec, conn, lu_cmd, lbpos, data); if (data_len != 0) { /* sense data build by function */ lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return data_len; } /* complete locate command, new position to lbpos */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_scsi_read_position(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, int sa, uint8_t *data) { uint64_t lbpos; int data_len; lbpos = spec->lbpos; switch (sa) { case 0x00: /* 0x00 SHORT FORM -- BLOCK ID */ case 0x01: /* 0x01 SHORT FORM -- VENDOR-SPECIFIC */ data_len = 20; memset(&data[0], 0, data_len); /* BOP(7) EOP(6) LOCU(5) BYCU(4) LOLU(2) PERR(1) */ /* only one partision is supported, BOT/EOT equal BOP/EOP */ if (lbpos == 0ULL) { BSET8(&data[0], 7); /* BOP=1 */ } if (spec->eom) { BSET8(&data[0], 6); /* EOP=1 */ } /* logical object count unknown */ BSET8(&data[0], 5); /* LOCU=1 */ /* byte count unknown */ BSET8(&data[0], 4); /* BYCU=1 */ /* logical object location unknown */ //BSET8(&data[0], 2); /* LOLU=1 */ if (lbpos > 0xffffffffULL) { BSET8(&data[0], 0); /* PERR=1 */ } /* PARTITION NUMBER */ data[1] = 0; /* FIRST LOGICAL OBJECT LOCATION */ DSET32(&data[4], (uint32_t)lbpos); /* LAST LOGICAL OBJECT LOCATION */ DSET32(&data[8], 0); /* NUMBER OF LOGICAL OBJECTS IN OBJECT BUFFER */ DSET24(&data[13], 0); /* NUMBER OF BYTES IN OBJECT BUFFER */ DSET32(&data[16], 0); break; case 0x06: /* LONG FORM */ data_len = 32; memset(&data[0], 0, data_len); /* BOP(7) EOP(6) MPU(3) LONU(2) */ /* only one partision is supported, BOT/EOT equal BOP/EOP */ if (lbpos == 0ULL) { BSET8(&data[0], 7); /* BOP=1 */ } if (spec->eom) { BSET8(&data[0], 6); /* EOP=1 */ } /* mark position unknown */ BSET8(&data[0], 3); /* MPU=1 */ /* logical object number unknown */ //BSET8(&data[0], 2); /* LONU=1 */ /* PARTITION NUMBER */ DSET32(&data[4], 0); /* LOGICAL OBJECT NUMBER */ DSET64(&data[8], lbpos); /* LOGICAL FILE IDENTIFIER */ DSET64(&data[16], 0ULL); /* LOGICAL SET IDENTIFIER */ DSET64(&data[24], 0ULL); break; default: /* INVALID FIELD IN CDB */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->sense_data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } lu_cmd->data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_build_sense_data(ISTGT_LU_TAPE *spec, uint8_t *data, int sk, int asc, int ascq) { uint8_t *cp; int hlen = 0, len = 0, plen; int total; int data_len; data_len = istgt_lu_scsi_build_sense_data(data, sk, asc, ascq); hlen = 2; if (data_len < (hlen + 18)) { return data_len; } cp = &data[hlen + len]; len = 8; /* FILEMARK(7) EOM(6) ILI(5) SENSE KEY(3-0) */ if (spec != NULL && spec->eof) { BSET8(&cp[2], 7); /* FILEMARK=1 */ } if (spec != NULL && spec->eom) { BSET8(&cp[2], 6); /* EOM=1 */ } /* Additional sense bytes */ /* for DLT8000 */ /* Internal Status Code */ cp[18] = 0; //cp[18] = 0x86; /* Directory Bad */ /* Tape Motion Hours */ DSET16(&cp[19], 0); /* Power On Hours */ DSET32(&cp[21], 0); /* Tape Remaining */ DSET32(&cp[25], 0); //DSET32(&cp[25], (uint32_t) (spec->size / spec->ctlblock->blocklen)); /* Reserved */ cp[29] = 0; plen = 30 - len; /* ADDITIONAL SENSE LENGTH */ cp[7] = plen; total = hlen + len + plen; /* SenseLength */ DSET16(&data[0], total - 2); return total; } static int istgt_lu_tape_build_sense_media(ISTGT_LU_TAPE *spec, uint8_t *data) { uint8_t *sense_data; int *sense_len; int data_len; sense_data = data; sense_len = &data_len; *sense_len = 0; if (!spec->mload && !spec->mchanged) { /* MEDIUM NOT PRESENT */ BUILD_SENSE(NOT_READY, 0x3a, 0x00); return data_len; } if (spec->mchanged) { /* MEDIUM NOT PRESENT */ BUILD_SENSE(NOT_READY, 0x3a, 0x00); return data_len; #if 0 /* LOGICAL UNIT NOT READY, CAUSE NOT REPORTABLE */ BUILD_SENSE(NOT_READY, 0x04, 0x00); return data_len; /* LOGICAL UNIT IS IN PROCESS OF BECOMING READY */ BUILD_SENSE(NOT_READY, 0x04, 0x01); return data_len; #endif } return 0; } static int istgt_lu_tape_variable_lbread(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lblen) { tape_markblock_t *mbp; uint8_t *data; uint64_t mediasize; uint64_t tape_leader; uint64_t marklen, alignment, padlen; uint64_t lbpos, offset, prev; uint64_t blen; uint64_t total; uint64_t request_len; uint32_t u; int64_t rc; mediasize = spec->size; tape_leader = spec->ctlblock->ctlblocklen; marklen = spec->ctlblock->marklen; alignment = spec->ctlblock->alignment; lbpos = spec->lbpos; offset = spec->offset; mbp = (tape_markblock_t *) lu_cmd->iobuf; data = (uint8_t *) lu_cmd->iobuf + marklen; total = 0ULL; u = 0; /* header + data + EOD */ request_len = marklen + lblen + marklen; spec->info = (uint32_t) lblen; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Read: %"PRIu64" (%"PRIu64")\n", lblen, offset); #endif /* TAPE_DEBUG */ if (request_len > lu_cmd->iobufsize) { ISTGT_ERRLOG("request_len(%"PRIu64") > iobufsize(%u)\n", request_len, lu_cmd->iobufsize); return -1; } /* read media check */ if (istgt_lu_tape_read_media_check(spec, conn, lu_cmd, request_len) < 0) { /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], (uint32_t) spec->info); /* not I/O error */ return 0; } /* position to virtual tape mark */ if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } /* virtual tape mark */ rc = istgt_lu_tape_read_native_mark(spec, mbp); if (rc < 0) { ISTGT_ERRLOG("lu_tape_read_native_mark() failed\n"); return -1; } if (!istgt_lu_tape_valid_mark_magic(mbp)) { ISTGT_ERRLOG("bad magic offset %"PRIu64"\n", offset); return -1; } if (lbpos != mbp->lbpos) { ISTGT_ERRLOG("bad position offset %"PRIu64" lbpos %"PRIu64 " mlbpos %"PRIu64"\n", offset, lbpos, mbp->lbpos); return -1; } if (memcmp(mbp->magic, MARK_EOFMAGIC, MARK_MAGICLEN) == 0) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "EOF found\n"); #endif /* TAPE_DEBUG */ /* EOF detected */ spec->eof = 1; goto early_return; } if (memcmp(mbp->magic, MARK_EODMAGIC, MARK_MAGICLEN) == 0) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "EOD found\n"); #endif /* TAPE_DEBUG */ /* EOD detected */ spec->eod = 1; goto early_return; } /* user data */ rc = istgt_lu_tape_read(spec, data + total, mbp->lblen); if (rc < 0 || rc != mbp->lblen) { ISTGT_ERRLOG("lu_tape_read() failed: rc %d\n", rc); return -1; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "read mlbpos=%"PRIu64", lblen=%"PRIu64 ", offset=%"PRIu64"\n", mbp->lbpos, mbp->lblen, offset); #endif /* TAPE_DEBUG */ /* 1 block OK */ spec->info -= (uint32_t) lblen; /* next offset to read */ prev = offset; offset += marklen + mbp->lblen; if (offset % alignment) { padlen = alignment; padlen -= offset % alignment; offset += padlen; } lbpos++; /* update information */ spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; if (lblen > mbp->lblen) { blen = mbp->lblen; } else { blen = lblen; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Read %"PRIu64" bytes\n", blen); #endif /* TAPE_DEBUG */ total += blen; u++; early_return: #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Read %"PRIu64" bytes total\n", total); #endif /* TAPE_DEBUG */ lu_cmd->data = data; lu_cmd->data_len = total; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_fixed_lbread(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lblen, uint32_t count) { tape_markblock_t *mbp; uint8_t *data; uint64_t mediasize; uint64_t tape_leader; uint64_t marklen, alignment, padlen; uint64_t lbpos, offset, prev; uint64_t blen; uint64_t total; uint64_t request_len; uint64_t rest; uint32_t u; int data_len; int64_t rc; mediasize = spec->size; tape_leader = spec->ctlblock->ctlblocklen; marklen = spec->ctlblock->marklen; alignment = spec->ctlblock->alignment; lbpos = spec->lbpos; offset = spec->offset; mbp = (tape_markblock_t *) lu_cmd->iobuf; data = (uint8_t *) lu_cmd->iobuf + marklen; total = 0ULL; /* (header + data) x N + EOD */ request_len = ((marklen + lblen) * (uint64_t) count) + marklen; spec->info = count; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Read: %"PRIu64" x %u (%"PRIu64")\n", lblen, count, offset); #endif /* TAPE_DEBUG */ if (request_len > lu_cmd->iobufsize) { ISTGT_ERRLOG("request_len(%"PRIu64") > iobufsize(%u)\n", request_len, lu_cmd->iobufsize); return -1; } /* read media check */ if (istgt_lu_tape_read_media_check(spec, conn, lu_cmd, request_len) < 0) { /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], (uint32_t) spec->info); /* not I/O error */ return 0; } /* position to virtual tape mark */ if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } rest = 0ULL; /* read N blocks */ for (u = 0; u < count; u++) { if (rest == 0) { /* virtual tape mark */ rc = istgt_lu_tape_read_native_mark(spec, mbp); if (rc < 0) { ISTGT_ERRLOG("lu_tape_read_native_mark() failed\n"); return -1; } if (!istgt_lu_tape_valid_mark_magic(mbp)) { ISTGT_ERRLOG("bad magic offset %"PRIu64"\n", offset); return -1; } if (lbpos != mbp->lbpos) { ISTGT_ERRLOG("bad position offset %"PRIu64" lbpos %"PRIu64 " mlbpos %"PRIu64"\n", offset, lbpos, mbp->lbpos); return -1; } if (memcmp(mbp->magic, MARK_EOFMAGIC, MARK_MAGICLEN) == 0) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "EOF found\n"); #endif /* TAPE_DEBUG */ /* EOF detected */ spec->eof = 1; goto early_return; } if (memcmp(mbp->magic, MARK_EODMAGIC, MARK_MAGICLEN) == 0) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "EOD found\n"); #endif /* TAPE_DEBUG */ /* EOD detected */ spec->eod = 1; goto early_return; } /* user data */ rc = istgt_lu_tape_read(spec, data + total, mbp->lblen); if (rc < 0 || rc != mbp->lblen) { ISTGT_ERRLOG("lu_tape_read() failed: rc %d\n", rc); return -1; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "read mlbpos=%"PRIu64", lblen=%" PRIu64", offset=%"PRIu64"\n", mbp->lbpos, mbp->lblen, offset); #endif /* TAPE_DEBUG */ rest = mbp->lblen; } /* check logical block size */ if ((rest > lblen * (count - u)) || rest < lblen) { /* incorrect length */ data_len = istgt_lu_tape_build_sense_data(spec, lu_cmd->sense_data, ISTGT_SCSI_SENSE_NO_SENSE, 0x00, 0x00); BSET8(&lu_cmd->sense_data[2+2], 5); /* ILI=1 */ //spec->info = count - u; /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], spec->info); lu_cmd->sense_data_len = data_len; lu_cmd->data = data; lu_cmd->data_len = total; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } else { /* 1 block OK */ spec->info--; rest -= lblen; blen = lblen; } /* buffer empty? */ if (rest == 0) { /* next offset to read */ prev = offset; offset += marklen + mbp->lblen; if (offset % alignment) { padlen = alignment; padlen -= offset % alignment; offset += padlen; } lbpos++; /* update information */ spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Read %"PRIu64" bytes\n", blen); #endif /* TAPE_DEBUG */ total += blen; } early_return: #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Read %"PRIu64" bytes total\n", total); #endif /* TAPE_DEBUG */ lu_cmd->data = data; lu_cmd->data_len = total; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_variable_lbwrite(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lblen) { tape_markblock_t *mbp; uint8_t *data; uint64_t mediasize; uint64_t tape_leader; uint64_t marklen, alignment, padlen; uint64_t lbpos, offset, prev; uint64_t total; uint64_t request_len; int64_t rc; mediasize = spec->size; tape_leader = spec->ctlblock->ctlblocklen; marklen = spec->ctlblock->marklen; alignment = spec->ctlblock->alignment; lbpos = spec->lbpos; offset = spec->offset; prev = spec->prev; mbp = (tape_markblock_t *) lu_cmd->iobuf; data = (uint8_t *) lu_cmd->iobuf + marklen; total = 0ULL; /* header + data + EOD */ request_len = marklen + lblen + marklen; spec->info = (uint32_t) lblen; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Write: %"PRIu64" (%"PRIu64")\n", lblen, offset); #endif /* TAPE_DEBUG */ if (request_len > lu_cmd->iobufsize) { ISTGT_ERRLOG("request_len(%"PRIu64") > iobufsize(%u)\n", request_len, lu_cmd->iobufsize); return -1; } /* prepare mark */ memset(mbp, 0, marklen); memcpy(mbp->magic, MARK_DATAMAGIC, MARK_MAGICLEN); mbp->endian = MARK_ENDIAN; mbp->version = MARK_VERSION; mbp->marklen = marklen; mbp->lblen = lblen; if (spec->compression) { /* not supported yet */ mbp->compalgo = spec->compalgo; mbp->vtcompalgo = MARK_COMPALGO_NONE; mbp->vtdecomplen = 0ULL; } else { mbp->compalgo = 0ULL; mbp->vtcompalgo = MARK_COMPALGO_NONE; mbp->vtdecomplen = 0ULL; } mbp->lbpos = lbpos; mbp->offset = offset; mbp->prev = prev; /* DATAOUT */ rc = istgt_lu_tape_transfer_data(conn, lu_cmd, data, lu_cmd->iobufsize - marklen, lblen); if (rc < 0) { ISTGT_ERRLOG("lu_tape_transfer_data() failed\n"); return -1; } /* write media check */ if (istgt_lu_tape_write_media_check(spec, conn, lu_cmd, request_len) < 0) { /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], (uint32_t) spec->info); /* not I/O error */ return 0; } /* position to virtual tape mark */ if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "write mlbpos=%"PRIu64", lblen=%"PRIu64 ", offset=%"PRIu64"\n", mbp->lbpos, mbp->lblen, offset); #endif /* TAPE_DEBUG */ /* virtual tape mark */ rc = istgt_lu_tape_write_native_mark(spec, mbp); if (rc < 0) { ISTGT_ERRLOG("lu_tape_write_native_mark() failed\n"); return -1; } /* user data */ rc = istgt_lu_tape_write(spec, data + total, lblen); if (rc != lblen) { ISTGT_ERRLOG("lu_tape_write() failed\n"); return -1; } /* 1 block OK */ spec->info -= (uint32_t) lblen; /* next offset to read */ prev = offset; offset += marklen + mbp->lblen; if (offset % alignment) { padlen = alignment; padlen -= offset % alignment; offset += padlen; } lbpos++; /* update information */ spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; mbp->lbpos = lbpos; mbp->offset = offset; mbp->prev = prev; total += lblen; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Wrote %"PRIu64" bytes\n", total); #endif /* TAPE_DEBUG */ lu_cmd->data_len = total; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } static int istgt_lu_tape_fixed_lbwrite(ISTGT_LU_TAPE *spec, CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd, uint64_t lblen, uint32_t count) { tape_markblock_t *mbp; uint8_t *data; uint64_t mediasize; uint64_t tape_leader; uint64_t marklen, alignment, padlen; uint64_t lbpos, offset, prev; uint64_t total; uint64_t request_len; uint32_t u; int64_t rc; mediasize = spec->size; tape_leader = spec->ctlblock->ctlblocklen; marklen = spec->ctlblock->marklen; alignment = spec->ctlblock->alignment; lbpos = spec->lbpos; offset = spec->offset; prev = spec->prev; mbp = (tape_markblock_t *) lu_cmd->iobuf; data = (uint8_t *) lu_cmd->iobuf + marklen; total = 0ULL; /* (header + data) x N + EOD */ request_len = ((marklen + lblen) * (uint64_t) count) + marklen; spec->info = count; #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Write: %"PRIu64" (%"PRIu64")\n", lblen, offset); #endif /* TAPE_DEBUG */ if (request_len > lu_cmd->iobufsize) { ISTGT_ERRLOG("request_len(%"PRIu64") > iobufsize(%u)\n", request_len, lu_cmd->iobufsize); return -1; } /* prepare mark */ memset(mbp, 0, marklen); memcpy(mbp->magic, MARK_DATAMAGIC, MARK_MAGICLEN); mbp->endian = MARK_ENDIAN; mbp->version = MARK_VERSION; mbp->marklen = marklen; mbp->lblen = lblen; if (spec->compression) { /* not supported yet */ mbp->compalgo = spec->compalgo; mbp->vtcompalgo = MARK_COMPALGO_NONE; mbp->vtdecomplen = 0ULL; } else { mbp->compalgo = 0ULL; mbp->vtcompalgo = MARK_COMPALGO_NONE; mbp->vtdecomplen = 0ULL; } mbp->lbpos = lbpos; mbp->offset = offset; mbp->prev = prev; /* DATAOUT */ rc = istgt_lu_tape_transfer_data(conn, lu_cmd, data, lu_cmd->iobufsize - marklen, lblen * count); if (rc < 0) { ISTGT_ERRLOG("lu_tape_transfer_data() failed\n"); return -1; } /* write media check */ if (istgt_lu_tape_write_media_check(spec, conn, lu_cmd, request_len) < 0) { /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], (uint32_t) spec->info); /* not I/O error */ return 0; } /* position to virtual tape mark */ if (istgt_lu_tape_seek(spec, (tape_leader + offset)) == -1) { ISTGT_ERRLOG("lu_tape_seek() failed\n"); return -1; } /* write N blocks */ for (u = 0; u < count; u++) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "write mlbpos=%"PRIu64", lblen=%"PRIu64 ", offset=%"PRIu64"\n", mbp->lbpos, mbp->lblen, offset); #endif /* TAPE_DEBUG */ /* virtual tape mark */ rc = istgt_lu_tape_write_native_mark(spec, mbp); if (rc < 0) { ISTGT_ERRLOG("lu_tape_write_native_mark() failed\n"); return -1; } /* user data */ rc = istgt_lu_tape_write(spec, data + total, lblen); if (rc != lblen) { ISTGT_ERRLOG("lu_tape_write() failed\n"); return -1; } /* 1 block OK */ spec->info--; /* next offset to read */ prev = offset; offset += marklen + mbp->lblen; if (offset % alignment) { padlen = alignment; padlen -= offset % alignment; offset += padlen; } lbpos++; /* update information */ spec->lbpos = lbpos; spec->prev = prev; spec->offset = offset; mbp->lbpos = lbpos; mbp->offset = offset; mbp->prev = prev; total += lblen; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_LU, "Wrote %"PRIu64" bytes\n", total); #endif /* TAPE_DEBUG */ lu_cmd->data_len = total; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } int istgt_lu_tape_reset(ISTGT_LU_Ptr lu, int lun) { ISTGT_LU_TAPE *spec; int flags; int rc; if (lu == NULL) { return -1; } if (lun >= lu->maxlun) { return -1; } if (lu->lun[lun].type == ISTGT_LU_LUN_TYPE_NONE) { return -1; } if (lu->lun[lun].type != ISTGT_LU_LUN_TYPE_REMOVABLE) { return -1; } spec = (ISTGT_LU_TAPE *) lu->lun[lun].spec; if (spec->lock) { ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "unlock by reset\n"); spec->lock = 0; } /* re-open file */ if (!spec->lu->readonly && !(spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) { rc = istgt_lu_tape_sync(spec, 0, spec->size); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_tape_sync() failed\n", lu->num, lun); /* ignore error */ } } rc = istgt_lu_tape_close(spec); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_tape_close() failed\n", lu->num, lun); /* ignore error */ } flags = (lu->readonly || (spec->mflags & ISTGT_LU_FLAG_MEDIA_READONLY)) ? O_RDONLY : O_RDWR; rc = istgt_lu_tape_open(spec, flags, 0666); if (rc < 0) { ISTGT_ERRLOG("LU%d: LUN%d: lu_tape_open() failed\n", lu->num, lun); return -1; } return 0; } int istgt_lu_tape_execute(CONN_Ptr conn, ISTGT_LU_CMD_Ptr lu_cmd) { ISTGT_LU_Ptr lu; ISTGT_LU_TAPE *spec; uint8_t *data; uint8_t *cdb; uint64_t fmt_lun; uint64_t lun; uint64_t method; uint32_t allocation_len; int data_len; int data_alloc_len; uint32_t transfer_len; uint8_t *sense_data; int *sense_len; int rc; if (lu_cmd == NULL) return -1; lu = lu_cmd->lu; if (lu == NULL) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } spec = NULL; cdb = lu_cmd->cdb; data = lu_cmd->data; data_alloc_len = lu_cmd->alloc_len; sense_data = lu_cmd->sense_data; sense_len = &lu_cmd->sense_data_len; *sense_len = 0; fmt_lun = lu_cmd->lun; method = (fmt_lun >> 62) & 0x03U; fmt_lun = fmt_lun >> 48; if (method == 0x00U) { lun = fmt_lun & 0x00ffU; } else if (method == 0x01U) { lun = fmt_lun & 0x3fffU; } else { lun = 0xffffU; } if (lun >= lu->maxlun) { #ifdef ISTGT_TRACE_TAPE ISTGT_ERRLOG("LU%d: LUN%4.4"PRIx64" invalid\n", lu->num, lun); #endif /* ISTGT_TRACE_TAPE */ if (cdb[0] == SPC_INQUIRY) { allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); /* PERIPHERAL QUALIFIER(7-5) PERIPHERAL DEVICE TYPE(4-0) */ BDSET8W(&data[0], 0x03, 7, 3); BDADD8W(&data[0], 0x1f, 4, 5); data_len = 96; memset(&data[1], 0, data_len - 1); /* ADDITIONAL LENGTH */ data[4] = data_len - 5; lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; return 0; } else { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } } spec = (ISTGT_LU_TAPE *) lu->lun[lun].spec; if (spec == NULL) { /* LOGICAL UNIT NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x25, 0x00); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64"\n", cdb[0], lu_cmd->lun); #ifdef ISTGT_TRACE_TAPE if (cdb[0] != SPC_TEST_UNIT_READY) { istgt_scsi_dump_cdb(cdb); } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "mload=%d, mchanged=%d, mwait=%d\n", spec->mload, spec->mchanged, spec->mwait); #endif /* ISTGT_TRACE_TAPE */ if (cdb[0] == SSC_WRITE_6 || cdb[0] == SSC_WRITE_FILEMARKS_6) { /* write operation (no sync) */ } else { /* non write operation */ if (spec->need_savectl || spec->need_writeeod) { /* flush pending data */ if (istgt_lu_tape_write_pending_data(spec, conn, lu_cmd) < 0) { ISTGT_ERRLOG("lu_tape_write_pending_data() failed\n"); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } } } switch (cdb[0]) { case SPC_INQUIRY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "INQUIRY\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } allocation_len = DGET16(&cdb[3]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_tape_scsi_inquiry(spec, conn, cdb, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "INQUIRY", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC_REPORT_LUNS: { int sel; ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REPORT LUNS\n"); if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); return -1; } sel = cdb[2]; ISTGT_TRACELOG(ISTGT_TRACE_DEBUG, "sel=%x\n", sel); allocation_len = DGET32(&cdb[6]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } if (allocation_len < 16) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } memset(data, 0, allocation_len); data_len = istgt_lu_tape_scsi_report_luns(lu, conn, cdb, sel, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACEDUMP(ISTGT_TRACE_DEBUG, "REPORT LUNS", data, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; } break; case SPC_TEST_UNIT_READY: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "TEST_UNIT_READY\n"); { data_len = istgt_lu_tape_build_sense_media(spec, sense_data); /* media state change? */ if (spec->mchanged) { /* wait OS polling */ if (spec->mwait > 0) { spec->mwait--; } else { /* load new media */ spec->mchanged = 0; spec->mload = 1; } } if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* OK media present */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_LOAD_UNLOAD: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LOAD_UNLOAD\n"); { int hold, eot, reten, load; hold = BGET8(&cdb[4], 3); eot = BGET8(&cdb[4], 2); reten = BGET8(&cdb[4], 1); load = BGET8(&cdb[4], 0); data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (load) { if (!spec->mload) { if (istgt_lu_tape_load_media(spec) < 0) { ISTGT_ERRLOG("lu_tape_load_media() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* OK load */ } if (hold) { /* loding tape to unit */ } else { /* loding tape to unit and potision to zero */ istgt_lu_tape_rewind(spec); } } else { if (hold) { /* if media in unit, position by eot,reten */ } else { /* unload tape from unit */ if (!spec->lock) { if (!spec->mload) { lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } if (istgt_lu_tape_unload_media(spec) < 0) { ISTGT_ERRLOG("lu_tape_unload_media() failed\n"); /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* OK unload */ } else { /* MEDIUM REMOVAL PREVENTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x53, 0x02); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } } } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_PREVENT_ALLOW_MEDIUM_REMOVAL: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "PREVENT_ALLOW_MEDIUM_REMOVAL\n"); { int persistent, prevent; persistent = BGET8(&cdb[4], 1); prevent = BGET8(&cdb[4], 0); data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (persistent) { if (prevent) { /* Persistent Prevent */ } else { /* Persistent Allow */ } } else { if (prevent) { /* Locked */ spec->lock = 1; } else { /* Unlocked */ spec->lock = 0; } } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_READ_BLOCK_LIMITS: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_BLOCK_LIMITS\n"); { if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); return -1; } data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } data_len = 6; /* GRANULARITY */ data[0] = 0; /* MAXIMUM BLOCK LENGTH LIMIT */ DSET24(&data[1], TAPE_MAXIMUM_BLOCK_LENGTH); /* MINIMUM BLOCK LENGTH LIMIT */ DSET16(&data[4], TAPE_MINIMUM_BLOCK_LENGTH); lu_cmd->data_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SELECT_6: { int pf, sp, pllen; int mdlen, mt, dsp, bdlen; pf = BGET8(&cdb[1], 4); sp = BGET8(&cdb[1], 0); pllen = cdb[4]; /* Parameter List Length */ /* Data-Out */ rc = istgt_lu_tape_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, pllen); if (rc < 0) { ISTGT_ERRLOG("lu_tape_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SELECT(6)", lu_cmd->iobuf, pllen); #endif data = lu_cmd->iobuf; mdlen = data[0]; /* Mode Data Length */ mt = data[1]; /* Medium Type */ dsp = data[2]; /* Device-Specific Parameter */ bdlen = data[3]; /* Block Descriptor Length */ if (bdlen > 0) { /* Short LBA mode parameter block descriptor */ /* data[4]-data[7] Number of Blocks */ /* data[8]-data[11] Block Length */ spec->lblen = (uint64_t) (DGET32(&data[8]) & 0x00ffffffU); #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "set to lblen=%"PRIu64"\n", spec->lblen); #endif /* TAPE_DEBUG */ } /* page data */ data_len = istgt_lu_tape_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[4 + bdlen], pllen - (4 + bdlen)); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = pllen; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SELECT_10: { int pf, sp, pllen; int mdlen, mt, dsp, bdlen; int llba; pf = BGET8(&cdb[1], 4); sp = BGET8(&cdb[1], 0); pllen = DGET16(&cdb[7]); /* Parameter List Length */ /* Data-Out */ rc = istgt_lu_tape_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, pllen); if (rc < 0) { ISTGT_ERRLOG("lu_tape_transfer_data() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SELECT(10)", lu_cmd->iobuf, pllen); #endif data = lu_cmd->iobuf; mdlen = DGET16(&data[0]); /* Mode Data Length */ mt = data[2]; /* Medium Type */ dsp = data[3]; /* Device-Specific Parameter */ llba = BGET8(&data[4], 0); /* Long LBA */ bdlen = DGET16(&data[6]); /* Block Descriptor Length */ if (llba) { if (bdlen > 0) { /* Long LBA mode parameter block descriptor */ /* data[8]-data[15] Number of Blocks */ /* data[16]-data[19] Reserved */ /* data[20]-data[23] Block Length */ spec->lblen = (uint64_t) DGET32(&data[20]); #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "set to lblen=%"PRIu64"\n", spec->lblen); #endif /* TAPE_DEBUG */ } } else { if (bdlen > 0) { /* Short LBA mode parameter block descriptor */ /* data[8]-data[11] Number of Blocks */ /* data[12]-data[15] Block Length */ spec->lblen = (uint64_t) (DGET32(&data[12]) & 0x00ffffffU); #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "set to lblen=%"PRIu64"\n", spec->lblen); #endif /* TAPE_DEBUG */ } } /* page data */ data_len = istgt_lu_tape_scsi_mode_select_page(spec, conn, cdb, pf, sp, &data[8 + bdlen], pllen - (8 + bdlen)); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = pllen; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SENSE_6: { int dbd, pc, page, subpage; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } dbd = BGET8(&cdb[1], 3); pc = BGET8W(&cdb[2], 7, 2); page = BGET8W(&cdb[2], 5, 6); subpage = cdb[3]; allocation_len = cdb[4]; if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_tape_scsi_mode_sense6(spec, conn, cdb, dbd, pc, page, subpage, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SENSE(6)", data, data_len); #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_MODE_SENSE_10: { int dbd, pc, page, subpage; int llbaa; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } llbaa = BGET8(&cdb[1], 4); dbd = BGET8(&cdb[1], 3); pc = BGET8W(&cdb[2], 7, 2); page = BGET8W(&cdb[2], 5, 6); subpage = cdb[3]; allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_tape_scsi_mode_sense10(spec, conn, cdb, llbaa, dbd, pc, page, subpage, data, data_alloc_len); if (data_len < 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("MODE SENSE(10)", data, data_len); #endif lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SPC_LOG_SELECT: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LOG_SELECT\n"); /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; case SPC_LOG_SENSE: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LOG_SENSE\n"); #if 0 /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); /* INVALID FIELD IN PARAMETER LIST */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x00); /* PARAMETER NOT SUPPORTED */ BUILD_SENSE(ILLEGAL_REQUEST, 0x26, 0x01); #endif /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; case SPC_REQUEST_SENSE: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REQUEST_SENSE\n"); { int desc; int sk, asc, ascq; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } desc = BGET8(&cdb[1], 0); if (desc != 0) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } data_len = istgt_lu_tape_build_sense_media(spec, sense_data); /* media state change? */ if (spec->mchanged) { /* wait OS polling */ if (spec->mwait > 0) { spec->mwait--; } else { /* load new media */ spec->mchanged = 0; spec->mload = 1; } } if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } allocation_len = cdb[4]; if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); if (!spec->sense) { /* NO ADDITIONAL SENSE INFORMATION */ sk = ISTGT_SCSI_SENSE_NO_SENSE; asc = 0x00; ascq = 0x00; } else { sk = (spec->sense >> 16) & 0xffU; asc = (spec->sense >> 8) & 0xffU; ascq = spec->sense & 0xffU; } data_len = istgt_lu_tape_build_sense_data(spec, sense_data, sk, asc, ascq); if (data_len < 0 || data_len < 2) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* omit SenseLength */ data_len -= 2; memcpy(data, sense_data + 2, data_len); lu_cmd->data_len = DMIN32(data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_ERASE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "ERASE_6\n"); { int xlong; data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } xlong = BGET8(&cdb[1], 0); if (!xlong) { /* short no operation */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } data_len = istgt_lu_tape_scsi_erase(spec, conn, lu_cmd, data); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_REWIND: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "REWIND\n"); { data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* position to BOT */ istgt_lu_tape_rewind(spec); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_SPACE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SPACE_6\n"); { int code; int count; data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } code = BGET8W(&cdb[1], 3, 4); count = istgt_convert_signed_24bits(DGET24(&cdb[2])); #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SPACE %d (code = %d)\n", count, code); #endif /* TAPE_DEBUG */ data_len = istgt_lu_tape_scsi_space(spec, conn, lu_cmd, code, count, data); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_WRITE_FILEMARKS_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_FILEMARKS_6\n"); { uint64_t request_len; uint64_t marklen; int wsmk; int count; data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } wsmk = BGET8(&cdb[1], 1); count = (int) DGET24(&cdb[2]); #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_FILEMARK %d\n", count); #endif /* TAPE_DEBUG */ if (wsmk) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (count == 0) { /* no mark but flush buffer */ if (spec->need_savectl || spec->need_writeeod) { /* flush pending data */ rc = istgt_lu_tape_write_pending_data(spec, conn, lu_cmd); if (rc < 0) { ISTGT_ERRLOG("lu_tape_write_pending_data() failed\n"); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return 0; } } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } if (spec->index + 1 + count > MAX_FILEMARKS - 1) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } istgt_lu_tape_prepare_offset(spec, conn, lu_cmd); if (spec->eom) { /* END-OF-PARTITION/MEDIUM DETECTED */ BUILD_SENSE(VOLUME_OVERFLOW, 0x00, 0x02); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* EOF x N + EOD */ marklen = spec->ctlblock->marklen; request_len = marklen * (uint64_t) count; request_len += marklen; /* write media check */ if (istgt_lu_tape_write_media_check(spec, conn, lu_cmd, request_len) < 0) { /* sense data build by function */ break; } /* actual wirte to media */ if (istgt_lu_tape_write_eof(spec, count, data) < 0) { ISTGT_ERRLOG("lu_tape_write_eof() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (istgt_lu_tape_write_eod(spec, data) < 0) { ISTGT_ERRLOG("lu_tape_write_eod() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } spec->need_writeeod = 0; if (istgt_lu_tape_save_ctlblock(spec) < 0) { ISTGT_ERRLOG("lu_tape_save_ctlblock() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } spec->need_savectl = 0; /* dynamic/extend media handle here */ /* Control + DATA(BOT/File/EOF) + EOD */ request_len = spec->ctlblock->ctlblocklen; request_len += spec->offset; request_len += marklen; if (istgt_lu_tape_shrink_media(spec, conn, lu_cmd, request_len, data) < 0) { ISTGT_ERRLOG("lu_tape_shrink_media() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* write done */ lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_READ_POSITION: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_POSITION\n"); { int sa; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } sa = BGET8W(&cdb[1], 4, 5); allocation_len = DGET16(&cdb[7]); if (allocation_len > data_alloc_len) { ISTGT_ERRLOG("data_alloc_len(%d) too small\n", data_alloc_len); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } memset(data, 0, allocation_len); data_len = istgt_lu_tape_scsi_read_position(spec, conn, lu_cmd, sa, data); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #if 0 istgt_dump("READ_POSITION", data, lu_cmd->data_len); #endif lu_cmd->data_len = DMIN32(lu_cmd->data_len, lu_cmd->transfer_len); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_LOCATE_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LOCATE_10\n"); { uint32_t loi; int bt, cp, partition; data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } bt = BGET8(&cdb[1], 2); cp = BGET8(&cdb[1], 1); loi = DGET32(&cdb[3]); partition = cdb[8]; if (cp) { /* INVALID FIELD IN CDB */ BUILD_SENSE(ILLEGAL_REQUEST, 0x24, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "LOCATE %u\n", loi); #endif /* TAPE_DEBUG */ data_len = istgt_lu_tape_scsi_locate(spec, conn, lu_cmd, loi, data); if (data_len != 0) { lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_READ_6: { int sili, fixed; uint64_t lblen; uint64_t request_len; uint64_t rest; if (lu_cmd->R_bit == 0) { ISTGT_ERRLOG("R_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } sili = BGET8(&cdb[1], 1); fixed = BGET8(&cdb[1], 0); transfer_len = DGET24(&cdb[2]); lblen = spec->lblen; if (fixed) { request_len = (uint64_t) transfer_len * lblen; } else { request_len = (uint64_t) transfer_len; } istgt_lu_tape_prepare_offset(spec, conn, lu_cmd); if (spec->eom) { /* END-OF-PARTITION/MEDIUM DETECTED */ BUILD_SENSE(MEDIUM_ERROR, 0x00, 0x02); /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], (uint32_t) transfer_len); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* clear EOF/EOD before reading */ spec->eof = spec->eod = 0; if (fixed) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_6 transfer %u x blocks %u SILI=%d\n", (uint32_t) lblen, (uint32_t) transfer_len, sili); rc = istgt_lu_tape_fixed_lbread(spec, conn, lu_cmd, lblen, (uint32_t) transfer_len); } else { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "READ_6 transfer %u SILI=%d\n", (uint32_t) transfer_len, sili); rc = istgt_lu_tape_variable_lbread(spec, conn, lu_cmd, transfer_len); } if (rc < 0) { ISTGT_ERRLOG("lu_tape_lbread() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (lu_cmd->status != ISTGT_SCSI_STATUS_GOOD) { /* sense data build by function */ break; } rest = request_len - lu_cmd->data_len; #if 0 istgt_dump("READ", lu_cmd->iobuf, 256); #endif if (spec->eof) { /* position to EOF */ spec->index++; spec->offset = spec->ctlblock->marks[spec->index].offset; spec->lbpos = spec->ctlblock->marks[spec->index].lbpos; spec->prev = spec->ctlblock->marks[spec->index].prev; /* position to next block of EOF */ spec->lbpos++; spec->prev = spec->offset; spec->offset += spec->ctlblock->marklen; /* FILEMARK DETECTED */ BUILD_SENSE(NO_SENSE, 0x00, 0x01); /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], spec->info); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (spec->eod) { /* END-OF-DATA DETECTED */ BUILD_SENSE(BLANK_CHECK, 0x00, 0x05); /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], spec->info); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (lu_cmd->data_len < request_len) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Underflow total=%u, transfer_len=%u, lblen=%u\n", lu_cmd->data_len, (uint32_t) request_len, (uint32_t) lblen); #endif /* TAPE_DEBUG */ /* over size? */ if (rest > spec->size || spec->offset > spec->size - rest) { spec->eom = 1; /* END-OF-PARTITION/MEDIUM DETECTED */ BUILD_SENSE(MEDIUM_ERROR, 0x00, 0x02); /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], spec->info); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } case SSC_WRITE_6: { int sili, fixed; uint64_t lblen; uint64_t request_len; uint64_t rest; int index_i; if (lu_cmd->W_bit == 0) { ISTGT_ERRLOG("W_bit == 0\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; return -1; } sili = BGET8(&cdb[1], 1); fixed = BGET8(&cdb[1], 0); transfer_len = DGET24(&cdb[2]); lblen = spec->lblen; if (fixed) { request_len = (uint64_t) transfer_len * lblen; } else { request_len = (uint64_t) transfer_len; } data_len = istgt_lu_tape_build_sense_media(spec, sense_data); if (data_len != 0) { rc = istgt_lu_tape_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, request_len); if (rc < 0) { ISTGT_ERRLOG("lu_tape_transfer_data() failed\n"); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } *sense_len = data_len; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } istgt_lu_tape_prepare_offset(spec, conn, lu_cmd); if (spec->eom) { rc = istgt_lu_tape_transfer_data(conn, lu_cmd, lu_cmd->iobuf, lu_cmd->iobufsize, request_len); if (rc < 0) { ISTGT_ERRLOG("lu_tape_transfer_data() failed\n"); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } /* END-OF-PARTITION/MEDIUM DETECTED */ BUILD_SENSE(VOLUME_OVERFLOW, 0x00, 0x02); /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], (uint32_t) transfer_len); lu_cmd->data_len = 0; lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (fixed) { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_6 transfer %u x blocks %u SILI=%d\n", (uint32_t) lblen, (uint32_t) transfer_len, sili); rc = istgt_lu_tape_fixed_lbwrite(spec, conn, lu_cmd, lblen, (uint32_t) transfer_len); } else { ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "WRITE_6 transfer %u SILI=%d\n", (uint32_t) transfer_len, sili); rc = istgt_lu_tape_variable_lbwrite(spec, conn, lu_cmd, transfer_len); } if (rc < 0) { ISTGT_ERRLOG("lu_tape_lbwrite() failed\n"); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } if (lu_cmd->status != ISTGT_SCSI_STATUS_GOOD) { /* sense data build by function */ break; } rest = request_len - lu_cmd->data_len; /* clean up marks after this file */ index_i = spec->index; if (spec->ctlblock->marks[index_i + 1].offset != MARK_END) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "save ctlblock and write EOD\n"); #endif /* TAPE_DEBUG */ spec->ctlblock->marks[index_i + 1].offset = MARK_END; spec->ctlblock->marks[index_i + 1].lbpos = MARK_END; spec->ctlblock->marks[index_i + 1].prev = spec->offset; if (istgt_lu_tape_save_ctlblock(spec) < 0) { ISTGT_ERRLOG("lu_tape_save_ctlblock() failed\n"); write_failure: /* INTERNAL TARGET FAILURE */ BUILD_SENSE(HARDWARE_ERROR, 0x44, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } request_len = spec->ctlblock->marklen; if (istgt_lu_tape_write_media_check(spec, conn, lu_cmd, request_len) < 0) { goto write_failure; } if (istgt_lu_tape_write_eod(spec, lu_cmd->data) < 0) { ISTGT_ERRLOG("lu_tape_write_eod() failed\n"); goto write_failure; } } else { /* pending some blocks for performance */ spec->ctlblock->marks[index_i + 1].prev = spec->offset; spec->need_savectl = 1; spec->need_writeeod = 1; } #if 0 if (spec->index == 2) { istgt_dump("WRITE", lu_cmd->iobuf, 256); } #endif if (lu_cmd->data_len < request_len) { #ifdef TAPE_DEBUG ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "Underflow total=%u, transfer_len=%u, lblen=%u\n", lu_cmd->data_len, (uint32_t) request_len, (uint32_t) lblen); #endif /* TAPE_DEBUG */ spec->eom = 1; /* WRITE ERROR */ BUILD_SENSE(MEDIUM_ERROR, 0x0c, 0x00); /* INFORMATION */ DSET32(&lu_cmd->sense_data[2+3], spec->info); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; } /* XXX TODO: fix */ case SPC2_RELEASE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_6\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RELEASE_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RELEASE_10\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RESERVE_6: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_6\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; case SPC2_RESERVE_10: ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "RESERVE_10\n"); lu_cmd->status = ISTGT_SCSI_STATUS_GOOD; break; default: ISTGT_ERRLOG("unsupported SCSI OP=0x%x\n", cdb[0]); /* INVALID COMMAND OPERATION CODE */ BUILD_SENSE(ILLEGAL_REQUEST, 0x20, 0x00); lu_cmd->status = ISTGT_SCSI_STATUS_CHECK_CONDITION; break; } ISTGT_TRACELOG(ISTGT_TRACE_SCSI, "SCSI OP=0x%x, LUN=0x%16.16"PRIx64" status=0x%x," " complete\n", cdb[0], lu_cmd->lun, lu_cmd->status); return 0; } istgt-20111008/doc/QUICKSTART.jp000644 000000 000000 00000005461 11312643107 015777 0ustar00rootwheel000000 000000 istgt.confの設定とistgtデーモンの起動 Portsからでもtarballからでもインストールを実行すると、 デフォルトで/usr/local/etc/istgtの中にサンプル定義ファイルが 作成されますのでこれをコピーして起動用の設定を作成します。 # cd /usr/local/etc/istgt # cp auth.conf.sample auth.conf # cp istgt.conf.sample istgt.conf # cp istgtcontrol.conf.sample istgtcontrol.conf 最低修正が必要な場所は以下の3箇所です。 [PortalGroup1] のサーバIPアドレス [InitiatorGroup1] のイニシエータ名とネットマスク [LogicalUnit1] のLUN0に割り当てる領域とサイズ [PortalGroup1] Portal DA1 192.168.2.36:3260 ↑192.168.2.36を利用するサーバのIPアドレスに変更します。 3260は標準のiSCSIポート番号なので通常は変更する必要はありません。 [InitiatorGroup1] InitiatorName "iqn.1991-05.com.microsoft:saturn" Netmask 192.168.2.0/24 ↑接続を許可するイニシエータ名とIPアドレスまたはCIDR表記に変更します。 任意の名前やIPを許可するには特殊ワード ALL を指定します。 [LogicalUnit1] LUN0 Storage /tank/iscsi/istgt-disk1 10GB ↑サンプルではファイル形式(/tank/iscsi/istgt-disk1)で 10GBを割り当てるようになっています。 環境に合わせてパスを変更し、ディスクの容量を指定します。 単位はMBやTBで指定することも可能です。 デバイスファイル(/dev/ad1, /dev/zvol/tank/volなど)を指定する場合は サイズにAutoを指定できます。 設定できたら起動スクリプトを使ってデーモンを起動します。 # /usr/local/etc/rc.d/istgt forcestart 初期化ルーチンのエラーは標準エラー出力に出されますが、 それ以降のエラーはsyslogに書き込むので/var/log/messagesも確認します。 正しく動くようになったら /etc/rc.conf に istgt_enable="YES" を加えてサーバ起動時にデーモンも自動起動するように設定します。 LUNに対するアクセス制限は以下のように適用されます。 IG(イニシエータグループ)で指定したイニシエータが ネットマスクで許可されたIPアドレスから、 PG(ポータルグループ)で指定したネットワークポータル(IP/ポート番号)を通して、 LU(iSCSI LUN)にアクセスした場合に許可されます。 イニシエータ名とIPアドレスの両方もしくはいずれかの設定により 許可するべき範囲を設定できるようになっています。 現時点では最低1個のアクセス許可を設定しないと起動しません。 ※LUというのは元々LUN0しか想定していない時代の名残で、 本来ならLUではなくLUGになるべきかもしれません。 istgt-20111008/doc/istgt.1.in000644 000000 000000 00000002474 11640373417 015604 0ustar00rootwheel000000 000000 .TH istgt 1 "Mar 12, 2009" "istgt" .SH NAME istgt \- iSCSI target .SH SYNOPSIS .B istgt [options] .SH DESCRIPTION .B istgt is an iSCSI target for FreeBSD 7.1 or later. It is designed for multipath failover cluster nodes. Also useful for virtual machine such as Hyper-V, ESXi. .SH OPTIONS .TP .BI \-c " config" Use specified file as configuration file. Multiple instance of istgt is allowed for different IP/ports. .TP .BI \-p " pidfile" Use specified file for writing PID of daemon. Also it can be specified in configuration file. This option overwrites by setting of configuration file. .TP .BI \-l " facility" Use specific syslog facility. .TP .BI \-m " mode" Use specified operational mode. 0 = traditional (similar to 20100707), 1 = normal (default), 2 = experimental (new feature mode). .TP .BI \-t " flag" Print trace information to standard error. trace flag (all, net, iscsi, scsi, lu, none) .TP .BI \-q Use quiet mode. The warning message is omitted. .TP .BI \-D The istgt will not detach from tty and run as foreground. It requires to print tarce information. .TP .BI \-H Show usage .TP .BI \-V Show version .SH FILES .PD 0 .TP .I %%CONFIGDIR%%/istgt.conf Configuration file .TP .I %%CONFIGDIR%%/auth.conf Authentication information file .TP .I /var/run/istgt.pid PID of daemon .PD .SH AUTHORS Daisuke Aoyama istgt-20111008/doc/Makefile.in000644 000000 000000 00000003307 11627501424 016020 0ustar00rootwheel000000 000000 # for doc top_srcdir = @top_srcdir@ srcdir = @srcdir@ prefix = @prefix@ exec_prefix = @exec_prefix@ bindir = @bindir@ sbindir = @sbindir@ sysconfdir = @sysconfdir@ datadir = @datarootdir@ libexecdir = @libexecdir@ mandir = $(prefix)/man configdir = @configdir@ mediadir = @mediadir@ SED = @SED@ source = header = document = QUICKSTART QUICKSTART.jp istgt.1.in sample = DISTDIR = $(top_srcdir)/`cat $(top_srcdir)/distdir` DISTFILES = Makefile.in \ $(header) $(source) \ $(document) $(sample) ######################################################################### all: istgt.1 istgt.1: $(SED) -e 's:%%PREFIX%%:$(prefix):g' \ -e 's:%%CONFIGDIR%%:$(configdir):g' \ -e 's:%%MEDIADIR%%:$(mediadir):g' < istgt.1.in > $@ dist: $(DISTFILES) mkdir $(DISTDIR)/$(subdir) cd $(srcdir); thisdir=`pwd`; \ for file in $(DISTFILES); do \ cp -p $$thisdir/$$file $(DISTDIR)/$(subdir); \ done install: install-dirs install -c -m 0644 istgt.1 $(DESTDIR)$(mandir)/man1/ -rm -f $(DESTDIR)$(mandir)/man1/istgt.1.gz gzip -9 $(DESTDIR)$(mandir)/man1/istgt.1 install-doc: install-doc-dirs install -c -m 0644 $(top_srcdir)/README $(DESTDIR)$(datadir)/doc/istgt/ install -c -m 0644 $(top_srcdir)/ChangeLog.jp $(DESTDIR)$(datadir)/doc/istgt/ install -c -m 0644 QUICKSTART $(DESTDIR)$(datadir)/doc/istgt/ install -c -m 0644 QUICKSTART.jp $(DESTDIR)$(datadir)/doc/istgt/ install-dirs: mkdir -p $(DESTDIR)$(mandir)/man1 install-doc-dirs: mkdir -p $(DESTDIR)$(datadir)/doc/istgt clean: -rm -f istgt.1 -rm -f a.out *.o *.core -rm -f *~ distclean: clean -rm -f Makefile config.status config.cache config.log config.h depend: ######################################################################### istgt-20111008/doc/QUICKSTART000644 000000 000000 00000002700 11312643107 015360 0ustar00rootwheel000000 000000 Quick Start ----------- Copy sample configuration files after installation. # cd /usr/local/etc/istgt # cp auth.conf.sample auth.conf # cp istgt.conf.sample istgt.conf # cp istgtcontrol.conf.sample istgtcontrol.conf Edit istgt.conf. 3 sections below at least need modification against your own copy of istgt.conf.sample. [PortalGroup1] IP address [InitiatorGroup1] Initiator name and netmask [LogicalUnit1] LUN0 allocated file name and size [PortalGroup1] Portal DA1 192.168.2.36:3260 Replace 192.168.2.36 with your server IP. Because 3260 is standard iSCSI port, 3260 does not need modification. [InitiatorGroup1] InitiatorName "iqn.1991-05.com.microsoft:saturn" Netmask 192.168.2.0/24 Replace "iqn.1991-05.com.microsoft:saturn" with your allowed initiator name and 192.168.2.0/24 with your allowed IP or CIDR. If you need any initiator names or IPs, use special word ALL instead. [LogicalUnit1] LUN0 Storage /tank/iscsi/istgt-disk1 10GB Replace /tank/iscsi/istgt-disk1 with your correct path and 10GB with the size you want. You can specify the size also with MB or TB. When the device file (/dev/ad1, /dev/zvol/tank/vol, etc.) is specified, Auto can be specified for the size. After finishing to edit istgt.conf, start daemon by the script. # /usr/local/etc/rc.d/istgt forcestart Check messages on terminal and syslog (usually /var/log/messages). An error message may only appear at syslog. Add istgt_enable="YES" to /etc/rc.conf. You have done.