From 80e759ccb4576444833746e465dfc3243c31f0b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=BA=86=E7=81=BF?= Date: Tue, 26 Jul 2022 10:36:55 +0800 Subject: [PATCH 01/21] Markdown head format --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 40bcac235..542bef62d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -#jSCSI - A Java iSCSI Framework +# jSCSI - A Java iSCSI Framework jSCSI is a feature-complete iSCSI implementation in Java only. Platform-independent and fast, jSCSI represents a premium example how low-level protocols can be pushed to higher levels. @@ -41,22 +41,22 @@ For further documentation and as an example, please refer to the examples in the * bundles: bundles containing the projects * pom.xml: Simple pom (yes we use Maven) -##License +## License This work is released in the public domain under the BSD 3-clause license -##Further information +## Further information The project is currently under refactoring, the documentation is accessible under http://jscsi.org (pointing to http://disy.github.com/jscsi/) and a mailinglist has been set up: https://mailman.uni-konstanz.de/mailman/listinfo/jscsi -##Publications +## Publications * A TechReport describes the second iteration of the framework: [PDF](http://nbn-resolving.de/urn:nbn:de:bsz:352-opus-84511) * The framework was presented at the Jazoon '07 as work in progress: [PDF](http://nbn-resolving.de/urn:nbn:de:bsz:352-opus-84424) * jSCSI acted as backend for a block visualization presented at the InfoVis 2006: [PDF](http://nbn-resolving.de/urn:nbn:de:bsz:352-opus-69096) -##Concluded Thesis +## Concluded Thesis * Target 1.0 (english): TO FOLLOW * Initiator 2.0 (german only): [PDF](http://nbn-resolving.de/urn:nbn:de:bsz:352-opus-130096) @@ -65,7 +65,7 @@ https://mailman.uni-konstanz.de/mailman/listinfo/jscsi Any questions, just contact sebastian.graf AT uni-konstanz.de -##Involved People +## Involved People jSCSI is maintained by: From a09e192c59511d4159343d70f35da863cad59715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=BA=86=E7=81=BF?= Date: Tue, 26 Jul 2022 10:38:48 +0800 Subject: [PATCH 02/21] Markdown head format for GitHub --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 542bef62d..0b3c17ea8 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ jSCSI contains a server (target), a client (initiator) and common classes to wor [![Build Status](https://secure.travis-ci.org/sebastiangraf/jSCSI.png)](http://travis-ci.org/sebastiangraf/jSCSI) -##Using jSCSI +## Using jSCSI * Get the latest jar over Github or Maven @@ -34,7 +34,7 @@ The schema and an example config are accessible as download as well and included For further documentation and as an example, please refer to the examples in the initiator- and target-module. -##Content +## Content * README: this readme file * LICENSE: license file From 3e15257aed395a92d81734280835807d05429575 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 29 Jul 2022 11:17:24 +0800 Subject: [PATCH 03/21] Fix redundant one block capacity --- .../connection/stage/fullfeature/ReadCapacityStage.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadCapacityStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadCapacityStage.java index f27dbf886..2d88e7bd9 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadCapacityStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadCapacityStage.java @@ -75,10 +75,10 @@ else if (opCode == ScsiOperationCode.READ_CAPACITY_16) // send PDU with requested READ CAPACITY parameter data ReadCapacityParameterData parameterData; if (cdb instanceof ReadCapacity10Cdb) - parameterData = new ReadCapacity10ParameterData(session.getStorageModule().getSizeInBlocks(),// returnedLogicalBlockAddress + parameterData = new ReadCapacity10ParameterData(session.getStorageModule().getSizeInBlocks() - 1,// returnedLogicalBlockAddress session.getStorageModule().getBlockSize());// logicalBlockLengthInBytes else - parameterData = new ReadCapacity16ParameterData(session.getStorageModule().getSizeInBlocks(),// returnedLogicalBlockAddress + parameterData = new ReadCapacity16ParameterData(session.getStorageModule().getSizeInBlocks() - 1,// returnedLogicalBlockAddress session.getStorageModule().getBlockSize());// logicalBlockLengthInBytes sendResponse(bhs.getInitiatorTaskTag(),// initiatorTaskTag, From 617fd3ab8153ae7366bf76824a6bfd22c36a95ac Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 29 Jul 2022 11:28:50 +0800 Subject: [PATCH 04/21] Add Unit Serial Number page into INQUIRY command to avoid error log when Linux initiator (initiator) login --- .../stage/fullfeature/InquiryStage.java | 9 ++- .../scsi/inquiry/SupportedVpdPages.java | 15 +++-- .../scsi/inquiry/UnitSerialNumberPage.java | 67 +++++++++++++++++++ 3 files changed, 83 insertions(+), 8 deletions(-) create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/UnitSerialNumberPage.java diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java index 5e219a9d2..068fe41c1 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java @@ -14,6 +14,7 @@ import org.jscsi.target.scsi.inquiry.PageCode.VitalProductDataPageName; import org.jscsi.target.scsi.inquiry.StandardInquiryData; import org.jscsi.target.scsi.inquiry.SupportedVpdPages; +import org.jscsi.target.scsi.inquiry.UnitSerialNumberPage; import org.jscsi.target.scsi.sense.senseDataDescriptor.senseKeySpecific.FieldPointerSenseKeySpecificData; import org.jscsi.target.settings.SettingsException; import org.jscsi.target.util.Debug; @@ -23,7 +24,7 @@ /** * A stage for processing INQUIRY SCSI commands. - * + * * @author Andreas Ergenzinger */ public class InquiryStage extends TargetFullFeatureStage { @@ -61,6 +62,7 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep if (illegalFieldPointers != null) { // an illegal request has been made LOGGER.error("illegal INQUIRY request"); + LOGGER.error("CDB bytes: \n" + Debug.byteBufferToString(parser.getCDB())); responsePdu = createFixedFormatErrorPdu(illegalFieldPointers, bhs.getInitiatorTaskTag(), parser.getExpectedDataTransferLength()); @@ -92,6 +94,9 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep case DEVICE_IDENTIFICATION : responseData = session.getTargetServer().getDeviceIdentificationVpdPage(); break; + case UNIT_SERIAL_NUMBER : + responseData = UnitSerialNumberPage.getInstance(); + break; default : // The initiator must not request unsupported mode pages. throw new InternetSCSIException(); @@ -100,7 +105,7 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep // send response sendResponse(bhs.getInitiatorTaskTag(), parser.getExpectedDataTransferLength(), responseData); - + } } diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java index 9ed07e0c2..4069b2dcf 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java @@ -11,7 +11,7 @@ * This Vital Product Data page contains a list of the VPD page codes supported by the logical unit. *

* This class uses the singleton pattern since the list of supported Vital Product Data page requests will never change. - * + * * @author Andreas Ergenzinger */ public final class SupportedVpdPages implements IResponseData { @@ -29,14 +29,15 @@ public final class SupportedVpdPages implements IResponseData { /* * determine which pages to support must be in ascending order see PAGECode.VitalProductDataPageName */ - public static final byte[] SUPPORTED_VPD_PAGES = new byte[] { (byte) 0x00,// SUPPORTED_VPD_PAGES, - // mandatory - (byte) 0x83,// DECIVE_IDENTIFICATION, mandatory + public static final byte[] SUPPORTED_VPD_PAGES = new byte[] { + (byte) 0x00,// SUPPORTED_VPD_PAGES, mandatory + (byte) 0x80,// UNIT_SERIAL_NUMBER + (byte) 0x83,// DECIVE_IDENTIFICATION, mandatory }; /** * Returns the singleton. - * + * * @return the singleton */ public static SupportedVpdPages getInstance () { @@ -48,6 +49,7 @@ private SupportedVpdPages () { // private due to singleton pattern } + @Override public void serialize (ByteBuffer byteBuffer, int index) { // *** byte 0 *** @@ -82,13 +84,14 @@ public void serialize (ByteBuffer byteBuffer, int index) { byteBuffer.put(SUPPORTED_VPD_PAGES[i]); } + @Override public int size () { return HEADER_SIZE + SUPPORTED_VPD_PAGES.length; } /** * Returns true for those and only for those VPD Page Codes which are supported by the jSCSI Target. - * + * * @param vitalProductDataPageName VPD Page Name whose support is inquired * @return true for those and only for those VPD Page Codes which are supported by the jSCSI Target */ diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/UnitSerialNumberPage.java b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/UnitSerialNumberPage.java new file mode 100644 index 000000000..5f7da397b --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/UnitSerialNumberPage.java @@ -0,0 +1,67 @@ +package org.jscsi.target.scsi.inquiry; + + +import java.nio.ByteBuffer; + +import org.jscsi.target.scsi.IResponseData; + + +/** + * The Unit Serial Number page provides a product serial number for the target or logical unit. + *

+ * This class uses the singleton pattern since the content of the Unit Serial Number page will never change. + * + * @author CHEN Qingcan + */ +public class UnitSerialNumberPage implements IResponseData { + + /** + * The singleton. + */ + private static UnitSerialNumberPage instance; + + /** + * Returns the singleton. + * + * @return the singleton + */ + public static synchronized UnitSerialNumberPage getInstance () { + if (instance == null) instance = new UnitSerialNumberPage (); + return instance; + } + + private UnitSerialNumberPage () { + // private due to singleton pattern + } + + private static final byte PAGE_CODE = (byte) 0x80; + private static final byte PAGE_LENGTH = (byte) 0x08; + + @Override + public void serialize (ByteBuffer out, int index) { + out.position (index); + // PERIPHERAL QUALIFIER and PERIPHERAL DEVICE TYPE + // See DeviceIdentificationVpdPage.peripheralQualifierAndPeripheralDeviceType for details. + out.put ((byte) 0); + // PAGE CODE (80h) + out.put (PAGE_CODE); + // Reserved + out.put ((byte) 0); + // PAGE LENGTH + // The PAGE LENGTH field specifies the length in bytes of the product serial number page. + // Older products that only support the Product Serial Number parameter will have a page length of 08h, + // while newer products that support both parameters will have a page length of 14h. + out.put (PAGE_LENGTH); + // Product Serial Number + // The Product Serial Number field contains ASCII data that is vendor-assigned serial number. + // The least significant ASCII character of the serial number shall appear as the last byte in the Data-In Buffer. + // If the product serial number is not available, the target shall return ASCII spaces (20h) in this field. + for (int i = 0 ; i < PAGE_LENGTH ; i++) out.put ((byte) 0x20); + } + + @Override + public int size () { + return 4 + PAGE_LENGTH; + } + +} From ff76938e4deb3e3511c7131edc97105de4a973f4 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 29 Jul 2022 11:35:20 +0800 Subject: [PATCH 05/21] Support PB level storage by adding READ (16), READ (12), WRITE (16), WRITE (12), REPORT SUPPORTED OPERATION CODES commands --- .../org/jscsi/parser/ProtocolDataUnit.java | 63 +++++++++++-------- .../phase/TargetFullFeaturePhase.java | 30 ++++++--- .../stage/fullfeature/ReadStage.java | 14 ++++- .../stage/fullfeature/ReportOpCodesStage.java | 57 +++++++++++++++++ .../stage/fullfeature/WriteStage.java | 20 +++--- .../org/jscsi/target/scsi/cdb/OpCodesCDB.java | 35 +++++++++++ .../org/jscsi/target/scsi/cdb/Read12Cdb.java | 46 ++++++++++++++ .../org/jscsi/target/scsi/cdb/Read16Cdb.java | 46 ++++++++++++++ .../target/scsi/cdb/ScsiOperationCode.java | 33 +++++++--- .../org/jscsi/target/scsi/cdb/Write12Cdb.java | 46 ++++++++++++++ .../org/jscsi/target/scsi/cdb/Write16Cdb.java | 46 ++++++++++++++ .../jscsi/target/scsi/report/OneOpCode.java | 40 ++++++++++++ .../java/org/jscsi/target/util/ReadWrite.java | 53 ++++++++++------ 13 files changed, 457 insertions(+), 72 deletions(-) create mode 100644 bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read12Cdb.java create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read16Cdb.java create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write12Cdb.java create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write16Cdb.java create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java diff --git a/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java b/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java index 7f16ea6d6..935404735 100644 --- a/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java +++ b/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -27,6 +27,7 @@ import java.util.ArrayList; import java.util.Iterator; +import com.google.common.io.BaseEncoding; import org.jscsi.exception.InternetSCSIException; import org.jscsi.parser.datasegment.AbstractDataSegment; import org.jscsi.parser.datasegment.IDataSegmentIterator.IDataSegmentChunk; @@ -39,7 +40,7 @@ *

ProtocolDataUnit

*

* This class encapsulates a Protocol Data Unit (PDU), which is defined in the iSCSI Standard (RFC 3720). - * + * * @author Volker Wildi */ public final class ProtocolDataUnit { @@ -95,7 +96,7 @@ public final class ProtocolDataUnit { /** * Default constructor, creates a new, empty ProtcolDataUnit object. - * + * * @param initHeaderDigest The instance of the digest to use for the Basic Header Segment protection. * @param initDataDigest The instance of the digest to use for the Data Segment protection. */ @@ -115,7 +116,7 @@ public ProtocolDataUnit (final IDigest initHeaderDigest, final IDigest initDataD /** * Serialize all informations of this PDU object to its byte representation. - * + * * @return The byte representation of this PDU. * @throws InternetSCSIException If any violation of the iSCSI-Standard emerge. * @throws IOException if an I/O error occurs. @@ -134,7 +135,7 @@ public final ByteBuffer serialize () throws InternetSCSIException , IOException } offset += serializeAdditionalHeaderSegments(pdu, offset); - + // write header digest // TODO: Move CRC calculation in BasicHeaderSegment.serialize? if (basicHeaderSegment.getParser().canHaveDigests()) { @@ -150,12 +151,12 @@ public final ByteBuffer serialize () throws InternetSCSIException , IOException offset += serializeDigest(pdu, dataDigest); } - return (ByteBuffer) pdu.rewind(); + return pdu.rewind(); } /** * Deserializes (parses) a given byte representation of a PDU to an PDU object. - * + * * @param pdu The byte representation of an PDU to parse. * @return The number of bytes, which are serialized. * @throws InternetSCSIException If any violation of the iSCSI-Standard emerge. @@ -178,7 +179,7 @@ public final int deserialize (final ByteBuffer pdu) throws InternetSCSIException /** * Deserializes a given array starting from offset 0 and store the informations in the * BasicHeaderSegment object.. - * + * * @param bhs The array to read from. * @throws InternetSCSIException If any violation of the iSCSI-Standard emerge. * @throws DigestException There is a mismatch of the digest. @@ -202,7 +203,7 @@ private final int deserializeBasicHeaderSegment (final ByteBuffer bhs) throws In /** * Deserializes a array (starting from offset 0) and store the informations to the * AdditionalHeaderSegment object. - * + * * @param pdu The array to read from. * @return The length of the read bytes. * @throws InternetSCSIException If any violation of the iSCSI-Standard emerge. @@ -215,7 +216,7 @@ private final int deserializeAdditionalHeaderSegments (final ByteBuffer pdu) thr /** * Deserializes a array (starting from the given offset) and store the informations to the * AdditionalHeaderSegment object. - * + * * @param pdu The ByteBuffer to read from. * @param offset The offset to start from. * @return The length of the written bytes. @@ -241,7 +242,7 @@ private final int deserializeAdditionalHeaderSegments (final ByteBuffer pdu, fin /** * Serialize all the contained additional header segments to the destination array starting from the given offset. - * + * * @param dst The destination array to write in. * @param offset The offset to start to write in dst. * @return The written length. @@ -259,7 +260,7 @@ private final int serializeAdditionalHeaderSegments (final ByteBuffer dst, final /** * Serializes the data segment (binary or key-value pairs) to a destination array, staring from offset to write. - * + * * @param dst The array to write in. * @param offset The start offset to start from in dst. * @return The written length. @@ -276,7 +277,7 @@ public final int serializeDataSegment (final ByteBuffer dst, final int offset) t /** * Deserializes a array (starting from the given offset) and store the informations to the Data Segment. - * + * * @param pdu The array to read from. * @param offset The offset to start from. * @return The length of the written bytes. @@ -312,7 +313,7 @@ private final int deserializeDataSegment (final ByteBuffer pdu, final int offset /** * Writes this ProtocolDataUnit object to the given SocketChannel. - * + * * @param sChannel SocketChannel to write to. * @return The number of bytes written, possibly zero. * @throws InternetSCSIException if any violation of the iSCSI-Standard emerge. @@ -337,7 +338,7 @@ public final int write (final SocketChannel sChannel) throws InternetSCSIExcepti /** * Reads from the given SocketChannel all the neccassary bytes to fill this PDU. - * + * * @param sChannel SocketChannel to read from. * @return The number of bytes, possibly zero,or -1 if the channel has reached end-of-stream * @throws IOException if an I/O error occurs. @@ -420,7 +421,7 @@ public final void clear () { /** * Returns an iterator to all contained Additional Header Segment in this PDU. - * + * * @return The iterator to the contained Additional Header Segment. * @see AdditionalHeaderSegment */ @@ -431,7 +432,7 @@ public final Iterator getAdditionalHeaderSegments () { /** * Returns the Basic Header Segment contained in this PDU. - * + * * @return The Basic Header Segment. * @see BasicHeaderSegment */ @@ -442,7 +443,7 @@ public final BasicHeaderSegment getBasicHeaderSegment () { /** * Gets the data segment in this PDU. - * + * * @return The data segment of this ProtocolDataUnit object. */ public final ByteBuffer getDataSegment () { @@ -458,7 +459,7 @@ public final void setDataSegment (final ByteBuffer dataSegment) { /** * Sets a new data segment in this PDU. - * + * * @param chunk The new data segment of this ProtocolDataUnit object. */ public final void setDataSegment (final IDataSegmentChunk chunk) { @@ -472,7 +473,7 @@ public final void setDataSegment (final IDataSegmentChunk chunk) { /** * Returns the instance of the used digest algorithm for the header. - * + * * @return The instance of the header digest. */ public final IDigest getHeaderDigest () { @@ -482,7 +483,7 @@ public final IDigest getHeaderDigest () { /** * Sets the digest of the header to use for data integrity. - * + * * @param newHeaderDigest An instance of the new header digest. */ public final void setHeaderDigest (final IDigest newHeaderDigest) { @@ -492,7 +493,7 @@ public final void setHeaderDigest (final IDigest newHeaderDigest) { /** * Returns the instance of the used digest algorithm for the data segment. - * + * * @return The instance of the data digest. */ public final IDigest getDataDigest () { @@ -502,7 +503,7 @@ public final IDigest getDataDigest () { /** * Sets the digest of the data segment to use for data integrity. - * + * * @param newDataDigest An instance of the new data segment digest. */ public final void setDataDigest (final IDigest newDataDigest) { @@ -516,15 +517,23 @@ public final void setDataDigest (final IDigest newDataDigest) { /** {@inheritDoc} */ @Override public final String toString () { - final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE); sb.append(basicHeaderSegment.toString()); - for (AdditionalHeaderSegment ahs : additionalHeaderSegments) { sb.append(ahs.toString()); } + if (dataSegment.limit () > 0) { + sb.append (" DataSegment (first 16 bytes): 0x"); + byte[] preview = new byte [Math.min (dataSegment.limit (), 16)]; + // NOT to use mark and reset because + // "If the mark is defined and larger than the new position then it is discarded." + int pos = dataSegment.position (); + dataSegment.position (0).get (preview).position (pos); + sb.append (BaseEncoding.base16 ().withSeparator (" ", 2).encode (preview)); + } + return sb.toString(); } @@ -567,7 +576,7 @@ public int hashCode() { /** * Calculates the needed size (in bytes) of serializing this object. - * + * * @return The needed size to store this object. */ private final int calcSize () { diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java b/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java index cd948da22..a52b3976a 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java @@ -20,6 +20,7 @@ import org.jscsi.target.connection.stage.fullfeature.ReadCapacityStage; import org.jscsi.target.connection.stage.fullfeature.ReadStage; import org.jscsi.target.connection.stage.fullfeature.ReportLunsStage; +import org.jscsi.target.connection.stage.fullfeature.ReportOpCodesStage; import org.jscsi.target.connection.stage.fullfeature.RequestSenseStage; import org.jscsi.target.connection.stage.fullfeature.SendDiagnosticStage; import org.jscsi.target.connection.stage.fullfeature.TargetFullFeatureStage; @@ -35,7 +36,7 @@ /** * Objects of this class represent the Target Full Feature Phase of a connection. - * + * * @see TargetPhase * @author Andreas Ergenzinger */ @@ -56,7 +57,7 @@ public final class TargetFullFeaturePhase extends TargetPhase { /** * The constructor. - * + * * @param connection {@inheritDoc} */ public TargetFullFeaturePhase (Connection connection) { @@ -66,7 +67,7 @@ public TargetFullFeaturePhase (Connection connection) { /** * Starts the full feature phase. - * + * * @return {@inheritDoc} * @throws OperationNotSupportedException {@inheritDoc} * @throws IOException {@inheritDoc} @@ -75,6 +76,7 @@ public TargetFullFeaturePhase (Connection connection) { * @throws DigestException {@inheritDoc} * @throws SettingsException {@inheritDoc} */ + @Override public boolean execute () throws DigestException , IOException , InterruptedException , InternetSCSIException , SettingsException { running = true; @@ -89,6 +91,7 @@ public boolean execute () throws DigestException , IOException , InterruptedExce if (connection.getTargetSession().isNormalSession()) { final SCSICommandParser parser = (SCSICommandParser) bhs.getParser(); ScsiOperationCode scsiOpCode = ScsiOperationCode.valueOf(parser.getCDB().get(0)); + int scsiServiceAction = parser.getCDB().get(1) & 0x1F; LOGGER.debug("scsiOpCode = " + scsiOpCode);// log SCSI // Operation Code @@ -121,21 +124,32 @@ public boolean execute () throws DigestException , IOException , InterruptedExce case SEND_DIAGNOSTIC : stage = new SendDiagnosticStage(this); break; - case READ_CAPACITY_10 :// use common read capacity stage - case READ_CAPACITY_16 : + case READ_CAPACITY_16 :// use common read capacity stage + case READ_CAPACITY_10 : stage = new ReadCapacityStage(this); break; - case WRITE_6 :// use common write stage + case WRITE_16 :// use common write stage + case WRITE_12 : case WRITE_10 : + case WRITE_6 : stage = new WriteStage(this); break; - case READ_6 :// use common read stage + case READ_16 :// use common read stage + case READ_12 : case READ_10 : + case READ_6 : stage = new ReadStage(this); break; case REPORT_LUNS : stage = new ReportLunsStage(this); break; + case REPORT_OP_CODES : + if (scsiServiceAction == 0x0C) { + stage = new ReportOpCodesStage(this); + } else { + scsiOpCode = null; + } + break; default : scsiOpCode = null; @@ -175,7 +189,7 @@ public boolean execute () throws DigestException , IOException , InterruptedExce return false; } - + /** * Stopping this phases execution */ diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadStage.java index 739345575..21165a89c 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReadStage.java @@ -13,17 +13,20 @@ import org.jscsi.target.connection.phase.TargetFullFeaturePhase; import org.jscsi.target.scsi.ScsiResponseDataSegment; import org.jscsi.target.scsi.cdb.Read10Cdb; +import org.jscsi.target.scsi.cdb.Read12Cdb; +import org.jscsi.target.scsi.cdb.Read16Cdb; import org.jscsi.target.scsi.cdb.Read6Cdb; import org.jscsi.target.scsi.cdb.ReadCdb; import org.jscsi.target.scsi.cdb.ScsiOperationCode; import org.jscsi.target.settings.SettingsException; +import org.jscsi.target.util.Debug; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A stage for processing READ (6) and READ (10) SCSI commands. - * + * * @author Andreas Ergenzinger */ public class ReadStage extends ReadOrWriteStage { @@ -54,7 +57,11 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep // get the Read(6) or Read(10) CDB ReadCdb cdb; final ScsiOperationCode scsiOpCode = ScsiOperationCode.valueOf(parser.getCDB().get(0)); - if (scsiOpCode == ScsiOperationCode.READ_10)// most likely option first + if (scsiOpCode == ScsiOperationCode.READ_16)// most likely option first + cdb = new Read16Cdb(parser.getCDB()); + else if (scsiOpCode == ScsiOperationCode.READ_12) + cdb = new Read12Cdb(parser.getCDB()); + else if (scsiOpCode == ScsiOperationCode.READ_10) cdb = new Read10Cdb(parser.getCDB()); else if (scsiOpCode == ScsiOperationCode.READ_6) cdb = new Read6Cdb(parser.getCDB()); @@ -71,7 +78,8 @@ else if (scsiOpCode == ScsiOperationCode.READ_6) if (cdb.getIllegalFieldPointers() != null) { // the command must fail - LOGGER.debug("illegal field in Read CDB"); + LOGGER.error("illegal field in Read CDB"); + LOGGER.error("CDB:\n" + Debug.byteBufferToString(parser.getCDB())); // create and send error PDU and leave stage final ProtocolDataUnit responsePdu = createFixedFormatErrorPdu(cdb.getIllegalFieldPointers(),// senseKeySpecificData diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java new file mode 100644 index 000000000..c441f69f9 --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java @@ -0,0 +1,57 @@ +package org.jscsi.target.connection.stage.fullfeature; + + +import java.io.IOException; +import java.security.DigestException; + +import org.jscsi.exception.InternetSCSIException; +import org.jscsi.parser.BasicHeaderSegment; +import org.jscsi.parser.ProtocolDataUnit; +import org.jscsi.parser.scsi.SCSICommandParser; +import org.jscsi.target.connection.phase.TargetFullFeaturePhase; +import org.jscsi.target.scsi.cdb.OpCodesCDB; +import org.jscsi.target.scsi.report.OneOpCode; +import org.jscsi.target.settings.SettingsException; +import org.jscsi.target.util.Debug; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A stage for processing REPORT SUPPORTED OPERATION CODES SCSI commands. + * + * @author CHEN Qingcan + */ +public class ReportOpCodesStage extends TargetFullFeatureStage { + + private static final Logger LOGGER = LoggerFactory.getLogger(ReportOpCodesStage.class); + + public ReportOpCodesStage (TargetFullFeaturePhase targetFullFeaturePhase) { + super(targetFullFeaturePhase); + } + + @Override + public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedException , InternetSCSIException , DigestException , SettingsException { + final BasicHeaderSegment bhs = pdu.getBasicHeaderSegment(); + final SCSICommandParser parser = (SCSICommandParser) bhs.getParser(); + + // get command details in CDB + if (LOGGER.isDebugEnabled()) {// print CDB bytes + LOGGER.debug("CDB bytes: \n" + Debug.byteBufferToString(parser.getCDB())); + } + final OpCodesCDB cdb = new OpCodesCDB(parser.getCDB()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("cdb.getAllocationLength() = " + cdb.getAllocationLength()); + LOGGER.debug("cdb.isNormalACA() = " + cdb.isNormalACA()); + LOGGER.debug("cdb.getReportingOptions() = " + cdb.getReportingOptions()); + } + + // send response + sendResponse ( + bhs.getInitiatorTaskTag(), + parser.getExpectedDataTransferLength(), + new OneOpCode (cdb.getReportingOptions()) + ); + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/WriteStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/WriteStage.java index e06857d88..96a2ffe18 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/WriteStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/WriteStage.java @@ -18,6 +18,8 @@ import org.jscsi.target.scsi.ScsiResponseDataSegment; import org.jscsi.target.scsi.cdb.ScsiOperationCode; import org.jscsi.target.scsi.cdb.Write10Cdb; +import org.jscsi.target.scsi.cdb.Write12Cdb; +import org.jscsi.target.scsi.cdb.Write16Cdb; import org.jscsi.target.scsi.cdb.Write6Cdb; import org.jscsi.target.scsi.cdb.WriteCdb; import org.jscsi.target.settings.SettingsException; @@ -27,8 +29,8 @@ /** - * A stage for processing WRITE (6) and WRITE (10) SCSI commands. - * + * A stage for processing WRITE (6), (10), (12), (16) SCSI commands. + * * @author Andreas Ergenzinger */ public final class WriteStage extends ReadOrWriteStage { @@ -47,7 +49,7 @@ public WriteStage (TargetFullFeaturePhase targetFullFeaturePhase) { /** * Is used for checking if the PDUs received in a Data-Out sequence actually are Data-Out PDU and if the PDUs have * been received in order. - * + * * @param parser the {@link AbstractMessageParser} subclass instance retrieved from the {@link ProtocolDataUnit}'s * {@link BasicHeaderSegment} * @throws InternetSCSIException if an unexpected PDU has been received @@ -90,7 +92,11 @@ public void execute (ProtocolDataUnit pdu) throws IOException , DigestException final int initiatorTaskTag = bhs.getInitiatorTaskTag(); WriteCdb cdb; final ScsiOperationCode scsiOpCode = ScsiOperationCode.valueOf(parser.getCDB().get(0)); - if (scsiOpCode == ScsiOperationCode.WRITE_10) + if (scsiOpCode == ScsiOperationCode.WRITE_16) + cdb = new Write16Cdb(parser.getCDB()); + else if (scsiOpCode == ScsiOperationCode.WRITE_12) + cdb = new Write12Cdb(parser.getCDB()); + else if (scsiOpCode == ScsiOperationCode.WRITE_10) cdb = new Write10Cdb(parser.getCDB()); else if (scsiOpCode == ScsiOperationCode.WRITE_6) cdb = new Write6Cdb(parser.getCDB()); @@ -117,9 +123,9 @@ else if (scsiOpCode == ScsiOperationCode.WRITE_6) * WriteStage is simply left early (without closing the connection), the initiator may send additional * unsolicited Data-Out PDUs, which the jSCSI Target is currently unable to ignore or process properly. */ - LOGGER.debug("illegal field in Write CDB"); - LOGGER.debug("CDB:\n" + Debug.byteBufferToString(parser.getCDB())); - + LOGGER.error("illegal field in Write CDB"); + LOGGER.error("CDB:\n" + Debug.byteBufferToString(parser.getCDB())); + // Not necessarily close the connection // create and send error PDU and leave stage diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java new file mode 100644 index 000000000..36d1d3cec --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java @@ -0,0 +1,35 @@ +package org.jscsi.target.scsi.cdb; + + +import java.nio.ByteBuffer; + +import org.jscsi.target.util.ReadWrite; + + +/** + * This class represents Command Descriptor Blocks for the REPORT SUPPORTED OPERATION CODES SCSI command. + * + * @author CHEN Qingcan + */ +public class OpCodesCDB extends CommandDescriptorBlock { + + private final int inReportingOptions; + private final int inAllocationLength; + + public OpCodesCDB (ByteBuffer buffer) { + super (buffer); + inReportingOptions = ReadWrite.readOneByteInt (buffer, 2) & 7; + inAllocationLength = ReadWrite.readFourByteInt (buffer, 6); + } + + /** REPORTING OPTIONS field */ + public int getReportingOptions () { + return inReportingOptions; + } + + /** ALLOCATION LENGTH field */ + public int getAllocationLength () { + return inAllocationLength; + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read12Cdb.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read12Cdb.java new file mode 100644 index 000000000..8a714b913 --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read12Cdb.java @@ -0,0 +1,46 @@ +package org.jscsi.target.scsi.cdb; + + +import java.nio.ByteBuffer; + +import org.jscsi.target.util.ReadWrite; + + +/** + * This class represents Command Descriptor Blocks for the READ (12) SCSI command. + * + *

+ * The READ (12) command requests that the device server read the specified logical block(s) and transfer them to + * the data-in buffer. Each logical block read includes user data and, if the medium is formatted with protection information + * enabled, protection information. Each logical block transferred includes user data and may include protection information, + * based on the RDPROTECT field and the medium format. + * + * @author CHEN Qingcan + */ +public final class Read12Cdb extends ReadCdb { + + public Read12Cdb (final ByteBuffer buffer) { + super(buffer);// OPERATION CODE + CONTROL + } + + @Override + protected long deserializeLogicalBlockAddress (ByteBuffer buffer) { + return ReadWrite.readUnsignedInt (buffer, 2); + } + + @Override + protected int deserializeTransferLength (ByteBuffer buffer) { + return ReadWrite.readFourByteInt (buffer, 6); + } + + @Override + protected int getLogicalBlockAddressFieldIndex () { + return 2; + } + + @Override + protected int getTransferLengthFieldIndex () { + return 6; + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read16Cdb.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read16Cdb.java new file mode 100644 index 000000000..e789fa5bf --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Read16Cdb.java @@ -0,0 +1,46 @@ +package org.jscsi.target.scsi.cdb; + + +import java.nio.ByteBuffer; + +import org.jscsi.target.util.ReadWrite; + + +/** + * This class represents Command Descriptor Blocks for the READ (16) SCSI command. + * + *

+ * The READ (16) command requests that the device server read the specified logical block(s) and transfer them to + * the data-in buffer. Each logical block read includes user data and, if the medium is formatted with protection information + * enabled, protection information. Each logical block transferred includes user data and may include protection information, + * based on the RDPROTECT field and the medium format. + * + * @author CHEN Qingcan + */ +public final class Read16Cdb extends ReadCdb { + + public Read16Cdb (final ByteBuffer buffer) { + super(buffer);// OPERATION CODE + CONTROL + } + + @Override + protected long deserializeLogicalBlockAddress (ByteBuffer buffer) { + return ReadWrite.readEightByteInt (buffer, 2); + } + + @Override + protected int deserializeTransferLength (ByteBuffer buffer) { + return ReadWrite.readFourByteInt (buffer, 10); + } + + @Override + protected int getLogicalBlockAddressFieldIndex () { + return 2; + } + + @Override + protected int getTransferLengthFieldIndex () { + return 10; + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java index 803981e3a..6cd7a83f0 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java @@ -9,12 +9,31 @@ * thirty-two command codes in each group. A total of 256 possible operation codes exist. *

* The value of the GROUP CODE field specifies the {@link CommandDescriptorBlock}'s length. - * + * * @see CdbType * @author Andreas Ergenzinger */ public enum ScsiOperationCode { - TEST_UNIT_READY((byte) 0x00), REQUEST_SENSE((byte) 0x03), FORMAT_UNIT((byte) 0x04), READ_6((byte) 0x08), WRITE_6((byte) 0x0a), INQUIRY((byte) 0x12), MODE_SELECT_6((byte) 0x15), MODE_SENSE_6((byte) 0x1a), SEND_DIAGNOSTIC((byte) 0x1d), READ_CAPACITY_10((byte) 0x25), READ_10((byte) 0x28), WRITE_10((byte) 0x2a), READ_CAPACITY_16((byte) 0x9e), REPORT_LUNS((byte) 0xa0); + TEST_UNIT_READY ((byte) 0x00), + REQUEST_SENSE ((byte) 0x03), + FORMAT_UNIT ((byte) 0x04), + READ_6 ((byte) 0x08), + WRITE_6 ((byte) 0x0a), + INQUIRY ((byte) 0x12), + MODE_SELECT_6 ((byte) 0x15), + MODE_SENSE_6 ((byte) 0x1a), + SEND_DIAGNOSTIC ((byte) 0x1d), + READ_CAPACITY_10 ((byte) 0x25), + READ_10 ((byte) 0x28), + WRITE_10 ((byte) 0x2a), + READ_16 ((byte) 0x88), + WRITE_16 ((byte) 0x8a), + READ_CAPACITY_16 ((byte) 0x9e), + REPORT_LUNS ((byte) 0xa0), + /** REPORT SUPPORTED OPERATION CODES */ + REPORT_OP_CODES ((byte) 0xa3), + READ_12 ((byte) 0xa8), + WRITE_12 ((byte) 0xaa); /** * The serialized value of the operation code. @@ -36,7 +55,7 @@ public enum ScsiOperationCode { /** * Returns the {@link ScsiOperationCode} corresponding to the passed byte value. - * + * * @param value the serialized value of a SCSI operation code * @return the corresponding {@link ScsiOperationCode} or null if the passed value is not known by the * jSCSI Target @@ -51,7 +70,7 @@ private ScsiOperationCode (final byte value) { /** * Returns the serialized value of the operation code. - * + * * @return the serialized value of the operation code */ public final byte value () { @@ -60,7 +79,7 @@ public final byte value () { /** * The three-bit GROUP CODE field provides for eight groups of command codes. - * + * * @return the three-bit GROUP CODE field */ public int getGroupCode () { @@ -69,7 +88,7 @@ public int getGroupCode () { /** * Returns the five-bit COMMAND CODE field. - * + * * @return the five-bit COMMAND CODE field */ public int getCommandCode () { @@ -78,7 +97,7 @@ public int getCommandCode () { /** * Returns the {@link CdbType} for this operation code. - * + * * @return the {@link CdbType} for this operation code */ public CdbType getCdbType () { diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write12Cdb.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write12Cdb.java new file mode 100644 index 000000000..e22778f14 --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write12Cdb.java @@ -0,0 +1,46 @@ +package org.jscsi.target.scsi.cdb; + + +import java.nio.ByteBuffer; + +import org.jscsi.target.util.ReadWrite; + + +/** + * This class represents Command Descriptor Blocks for the WRITE (12) SCSI command. + * + *

+ * The WRITE (12) command requests that the device server transfer the specified logical block(s) from the data-out + * buffer and write them. Each logical block transferred includes user data and may include protection information, based on the + * WRPROTECT field and the medium format. Each logical block written includes user data and, if the medium is formatted with + * protection information enabled, protection information. + * + * @author CHEN Qingcan + */ +public class Write12Cdb extends WriteCdb { + + public Write12Cdb (ByteBuffer buffer) { + super(buffer);// OPERATION CODE + CONTROL + } + + @Override + protected long deserializeLogicalBlockAddress (ByteBuffer buffer) { + return ReadWrite.readUnsignedInt (buffer, 2); + } + + @Override + protected int deserializeTransferLength (ByteBuffer buffer) { + return ReadWrite.readFourByteInt (buffer, 6); + } + + @Override + protected int getLogicalBlockAddressFieldIndex () { + return 2; + } + + @Override + protected int getTransferLengthFieldIndex () { + return 6; + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write16Cdb.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write16Cdb.java new file mode 100644 index 000000000..34924bfad --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/Write16Cdb.java @@ -0,0 +1,46 @@ +package org.jscsi.target.scsi.cdb; + + +import java.nio.ByteBuffer; + +import org.jscsi.target.util.ReadWrite; + + +/** + * This class represents Command Descriptor Blocks for the WRITE (16) SCSI command. + * + *

+ * The WRITE (16) command requests that the device server transfer the specified logical block(s) from the data-out + * buffer and write them. Each logical block transferred includes user data and may include protection information, based on the + * WRPROTECT field and the medium format. Each logical block written includes user data and, if the medium is formatted with + * protection information enabled, protection information. + * + * @author CHEN Qingcan + */ +public class Write16Cdb extends WriteCdb { + + public Write16Cdb (ByteBuffer buffer) { + super(buffer);// OPERATION CODE + CONTROL + } + + @Override + protected long deserializeLogicalBlockAddress (ByteBuffer buffer) { + return ReadWrite.readEightByteInt (buffer, 2); + } + + @Override + protected int deserializeTransferLength (ByteBuffer buffer) { + return ReadWrite.readFourByteInt (buffer, 10); + } + + @Override + protected int getLogicalBlockAddressFieldIndex () { + return 2; + } + + @Override + protected int getTransferLengthFieldIndex () { + return 10; + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java b/bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java new file mode 100644 index 000000000..e1b36131c --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java @@ -0,0 +1,40 @@ +package org.jscsi.target.scsi.report; + + +import java.nio.ByteBuffer; + +import org.jscsi.target.scsi.IResponseData; + +/** + * Supported one_command parameter data format. + * + * @author CHEN Qingcan + */ +public final class OneOpCode implements IResponseData { + + private final int reqOpCode; + + /** + * @param reqOpCode requested operation code + */ + public OneOpCode (int reqOpCode) { + this.reqOpCode = reqOpCode; + // TODO determine whether supported by reqOpCode + } + + @Override + public void serialize (ByteBuffer out, int index) { + out.position (index); + out.put ((byte) 0); + out.put ((byte) 0b011); // The device server supports the requested command in conformance with a SCSI standard. + out.put ((byte) 0); + out.put ((byte) 1); // CDB size + out.put ((byte) reqOpCode); // CDB usage data + } + + @Override + public int size () { + return 5; + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/util/ReadWrite.java b/bundles/target/src/main/java/org/jscsi/target/util/ReadWrite.java index 3294cb020..8f27126ae 100644 --- a/bundles/target/src/main/java/org/jscsi/target/util/ReadWrite.java +++ b/bundles/target/src/main/java/org/jscsi/target/util/ReadWrite.java @@ -2,12 +2,13 @@ import java.nio.ByteBuffer; +import java.nio.ByteOrder; /** * This utility class contains static methods for reading/writing integers of various lengths and character strings * from/to {@link ByteBuffer} objects and byte arrays. - * + * * @author Andreas Ergenzinger */ public final class ReadWrite { @@ -17,7 +18,7 @@ private ReadWrite() { /** * Reads a specified byte from a {@link ByteBuffer} and returns its value as an unsigned integer. - * + * * @param buffer the {@link ByteBuffer} containing the byte * @param start the index position of the byte in the {@link ByteBuffer} * @return the unsigned integer value of the byte @@ -29,7 +30,7 @@ public static final int readOneByteInt (ByteBuffer buffer, int start) { /** * Reads a specified byte from a byte array and returns its value as an unsigned integer. - * + * * @param array the array containing the byte * @param start the index position of the byte in the array * @return the unsigned integer value of the byte @@ -41,7 +42,7 @@ public static final int readOneByteInt (byte[] array, int start) { /** * Reads an unsigned 2-byte integer from two consecutive big-endian-ordered bytes of a {@link ByteBuffer} object and * returns the value. - * + * * @param buffer the {@link ByteBuffer} containing the bytes * @param start the index position of the most-significant byte * @return the unsigned value of the two-byte integer @@ -55,7 +56,7 @@ public static final int readTwoByteInt (ByteBuffer buffer, int start) { /** * Reads an unsigned 2-byte integer from two consecutive big-endian-ordered bytes of a byte array and returns the * value. - * + * * @param array the byte array containing the bytes * @param start the index position of the most-significant byte * @return the unsigned value of the two-byte integer @@ -67,7 +68,7 @@ public static final int readTwoByteInt (byte[] array, int start) { /** * Reads an unsigned 3-byte integer from three consecutive big-endian-ordered bytes of a {@link ByteBuffer} object * and returns the value. - * + * * @param buffer the {@link ByteBuffer} containing the bytes * @param start the index position of the most-significant byte * @return the unsigned value of the three-byte integer @@ -82,7 +83,7 @@ public static final int readThreeByteInt (ByteBuffer buffer, int start) { /** * Reads an unsigned 3-byte integer from three consecutive big-endian-ordered bytes of a byte array and returns the * value. - * + * * @param array the byte array containing the bytes * @param start the index position of the most-significant byte * @return the unsigned value of the three-byte integer @@ -94,7 +95,7 @@ public static final int readThreeByteInt (byte[] array, int start) { /** * Reads a (signed) 4-byte integer from four consecutive big-endian-ordered bytes of a {@link ByteBuffer} object and * returns the value. - * + * * @param buffer the {@link ByteBuffer} containing the bytes * @param start the index position of the most-significant byte * @return the value of the four-byte integer @@ -110,7 +111,7 @@ public static final int readFourByteInt (ByteBuffer buffer, int start) { /** * Reads a (signed) 4-byte integer from four consecutive big-endian-ordered bytes of a byte array and returns the * value. - * + * * @param array the byte array containing the bytes * @param start the index position of the most-significant byte * @return the unsigned value of the four-byte integer @@ -119,13 +120,25 @@ public static final int readFourByteInt (byte[] array, int start) { return ((array[start] & 255) << 24) | ((array[start + 1] & 255) << 16) | ((array[start + 2] & 255) << 8) | (array[start + 3] & 255); } + /** + * Reads a (signed) 8-byte integer from eight consecutive big-endian-ordered bytes of a {@link ByteBuffer} object and + * returns the value. + * + * @param buffer the {@link ByteBuffer} containing the bytes + * @param start the index position of the most-significant byte + * @return the value of the four-byte integer + */ + public static final long readEightByteInt (ByteBuffer buffer, int start) { + return buffer.order (ByteOrder.BIG_ENDIAN).getLong (start); + } + /** * Puts the characters in the passed String into an array of one or more ByteBuffers and returns it. *

* If the String does not end with a null character, one will be appended. If the String's length is larger than the * specified bufferSize, all but the last ByteBuffer will have capacity() = bufferSize, the last one * will contain the remaining String characters. - * + * * @param string the String to be put into the ByteBuffer * @param bufferSize the maximum size of the returned ByteBuffers * @return an array of ByteBuffers containing the passed String @@ -148,7 +161,7 @@ public static ByteBuffer[] stringToTextDataSegments (String string, int bufferSi /** * Puts the characters in the passed String into a ByteBuffer of equal length and returns it. - * + * * @param string any String * @return a ByteBuffer containing the characters of the passed String */ @@ -162,7 +175,7 @@ private static ByteBuffer stringToByteBuffer (final String string) { /** * Appends the content of a ProtocolDataUnit (text) data segment to a {@link StringBuilder}; - * + * * @param byteBuffer the PDU's data segment * @param stringBuilder the {@link StringBuilder} that will be extended */ @@ -174,9 +187,9 @@ public static final void appendTextDataSegmentToStringBuffer (final ByteBuffer b /** * Writes the given value to the buffer in big-endian format, with the index position of the most * significant byte being start. - * + * * To get the value back from the buffer, use readUnsignedInt. - * + * * @param value the integer to write to the ByteBuffer * @param buffer where the integer will be stored * @param start index of the most significant byte of the stored value @@ -191,7 +204,7 @@ public static final void writeInt (int value, final ByteBuffer buffer, int start /** * Returns the bytes in a {@link ByteBuffer} as a UTF-8 encoded {@link String}. - * + * * @param buffer a {@link ByteBuffer} containing UTF-8 encoded characters. * @return a String representation of the buffer's content */ @@ -202,7 +215,7 @@ public static String byteBufferToString (final ByteBuffer buffer) { /** * Splits the passed value into bytes and returns them in an array, in big-endian format. - * + * * @param value the long to split * @return byte representation of the parameter */ @@ -216,7 +229,7 @@ public static byte[] longToBytes (final long value) { /** * Writes the given value to the buffer in big-endian format, with the index position of the most * significant byte being index. - * + * * @param value the integer to write to the ByteBuffer * @param buffer where the integer will be stored * @param index index of the most significant byte of the stored value @@ -231,7 +244,7 @@ public static void writeLong (final ByteBuffer buffer, final long value, final i /** * Writes the two least-significant big-endian-ordered bytes of an integer the a specified position in a * {@link ByteBuffer}. - * + * * @param buffer where the bytes will be written * @param value the value to convert and copy * @param index the position of the most significant byte in the {@link ByteBuffer} @@ -245,7 +258,7 @@ public static void writeTwoByteInt (final ByteBuffer buffer, final int value, fi /** * Writes the three least-significant big-endian-ordered bytes of an integer the a specified position in a * {@link ByteBuffer}. - * + * * @param buffer where the bytes will be written * @param value the value to convert and copy * @param index the position of the most significant byte in the {@link ByteBuffer} @@ -261,7 +274,7 @@ public static void writeThreeByteInt (final ByteBuffer buffer, final int value, /** * Reads an unsigned 4-byte integer from four consecutive big-endian-ordered bytes of a {@link ByteBuffer} object * and returns the value. - * + * * @param buffer the {@link ByteBuffer} containing the bytes * @param start the index position of the most-significant byte * @return the value of the unsigned four-byte integer From 7acaa6f9c627b53c4fe573988ae07c69395a5e0c Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 29 Jul 2022 16:05:44 +0800 Subject: [PATCH 06/21] Ignore javadoc plugin errors --- pom.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/pom.xml b/pom.xml index d7fbb4a98..4d7c969d6 100644 --- a/pom.xml +++ b/pom.xml @@ -72,6 +72,7 @@ maven-javadoc-plugin + 2.7 attach-sources From a637f235bd09c19b125fd8be701142ea04fd48c2 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 29 Jul 2022 16:17:39 +0800 Subject: [PATCH 07/21] How to build --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/README.md b/README.md index 0b3c17ea8..4ec022b14 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,16 @@ For further documentation and as an example, please refer to the examples in the * bundles: bundles containing the projects * pom.xml: Simple pom (yes we use Maven) +## Build +* jar only: +```bash +mvn -Dmaven.test.skip=true -Dmaven.source.skip=true -Dmaven.javadoc.skip=true clean package +``` +* jar, javadoc and sources: +```bash +mvn -Dmaven.test.skip=true -Pdoclint-java8-disable clean package +``` + ## License This work is released in the public domain under the BSD 3-clause license From 36be0c84c1eda0a333002006f8b7945f1e042e89 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 5 Aug 2022 10:16:15 +0800 Subject: [PATCH 08/21] Fix RESIDUAL flag bit mask to prevent Microsoft Windows 10 bundled iSCSI initiator reset connection --- .../org/jscsi/parser/data/DataInParser.java | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/bundles/commons/src/main/java/org/jscsi/parser/data/DataInParser.java b/bundles/commons/src/main/java/org/jscsi/parser/data/DataInParser.java index 5f0d316b8..ff560be0e 100644 --- a/bundles/commons/src/main/java/org/jscsi/parser/data/DataInParser.java +++ b/bundles/commons/src/main/java/org/jscsi/parser/data/DataInParser.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -30,7 +30,7 @@ /** * This class parses a Data-In message defined in the iSCSI Standard (RFC3720). - * + * * @author Volker Wildi */ public class DataInParser extends TargetMessageParser { @@ -88,7 +88,7 @@ public class DataInParser extends TargetMessageParser { /** * Default constructor, creates a new, empty DataInParser object. - * + * * @param initProtocolDataUnit The reference ProtocolDataUnit instance, which contains this * DataInParser subclass object. */ @@ -175,7 +175,7 @@ public final boolean incrementSequenceNumber () { * have to be in increasing Buffer Offset order and overlays are forbidden.
* The ordering between sequences is determined by DataSequenceInOrder. When set to Yes, it means that sequences * have to be in increasing Buffer Offset order and overlays are forbidden. - * + * * @return The buffer offset of this DataInParser object. */ public int getBufferOffset () { @@ -191,7 +191,7 @@ public int getBufferOffset () { * For output (write) data PDUs, the DataSN is the Data-Out PDU number within the current output sequence. The * current output sequence is either identified by the Initiator Task Tag (for unsolicited data) or is a data * sequence generated for one R2T (for data solicited through R2T). - * + * * @return The Data Sequence Number of this DataInParser object. */ public int getDataSequenceNumber () { @@ -206,7 +206,7 @@ public int getDataSequenceNumber () { * Residual Count indicates the number of bytes that were not transferred because the initiator’s Expected Data * Transfer Length was not sufficient. If the U bit is set, the Residual Count indicates the number of bytes that * were not transferred out of the number of bytes expected to be transferred. - * + * * @return The Residual Count of this object. */ public int getResidualCount () { @@ -225,7 +225,7 @@ public int getResidualCount () { * is reserved and means that the Target Transfer Tag is not supplied. If the Target Transfer Tag is provided, then * the LUN field MUST hold a valid value and be consistent with whatever was specified with the command; otherwise, * the LUN field is reserved. - * + * * @return Returns the Target Transfer Tag of this DataInParser object. */ public int getTargetTaskTag () { @@ -251,7 +251,7 @@ public int getTargetTaskTag () { * initiator also MUST NOT acknowledge the status for the task before those holes are filled. A status * acknowledgement for a task that generated the Data-In PDUs is considered by the target as an implicit * acknowledgement of the Data-In PDUs if such an acknowledgement was requested by the target. - * + * * @return Returns true, if the AcknowledgeBit is set. Else false. */ public boolean isAcknowledgeFlag () { @@ -262,7 +262,7 @@ public boolean isAcknowledgeFlag () { /** * In this case, the Bidirectional Read Residual Count indicates the number of bytes that were not transferred to * the initiator because the initiator’s Expected Bidirectional Read Data Transfer Length was not sufficient. - * + * * @return True, if the ReadResidualOverflow-Flag of this object is set. Else false. */ public boolean isBidirectionalReadResidualOverflow () { @@ -273,7 +273,7 @@ public boolean isBidirectionalReadResidualOverflow () { /** * In this case, the Bidirectional Read Residual Count indicates the number of bytes that were not transferred to * the initiator out of the number of bytes expected to be transferred. - * + * * @return True, if the ReadResidualUnderflow-Flag of this object is set. Else false. */ public boolean isBidirectionalReadResidualUnderflow () { @@ -285,7 +285,7 @@ public boolean isBidirectionalReadResidualUnderflow () { * In this case, the Residual Count indicates the number of bytes that were not transferred because the initiator’s * Expected Data Transfer Length was not sufficient. For a bidirectional operation, the Residual Count contains the * residual for the write operation. - * + * * @return True, if the ResidualOverflow-Flag of this object is set. Else false. */ public boolean isResidualOverflow () { @@ -297,7 +297,7 @@ public boolean isResidualOverflow () { * In this case, the Residual Count indicates the number of bytes that were not transferred out of the number of * bytes that were expected to be transferred. For a bidirectional operation, the Residual Count contains the * residual for the write operation. - * + * * @return True, if the ResidualUnderflow-Flag of this object is set. Else false. */ public boolean isResidualUnderflow () { @@ -312,7 +312,7 @@ public boolean isResidualUnderflow () { * If a SCSI device error is detected while data from the initiator is still expected (the command PDU did not * contain all the data and the target has not received a Data PDU with the final bit Set), the target MUST wait * until it receives a Data PDU with the F bit set in the last expected sequence before sending the Response PDU. - * + * * @return The status code of this object. * @see SCSIStatus */ @@ -326,7 +326,7 @@ public SCSIStatus getStatus () { * F bit MUST also be set to 1.
* The fields StatSN, Status, and Residual Count only have meaningful content if the S bit is set to 1 * and their values are defined in Section 10.4 SCSI Response. - * + * * @return True, if the Status-Flag of this object is set. Else false. */ public boolean isStatusFlag () { @@ -462,10 +462,10 @@ protected int serializeBytes1to3 () { line |= ACKNOWLEDGE_FLAG_MASK; } if (residualOverflow) { - line |= Constants.READ_RESIDUAL_OVERFLOW_FLAG_MASK; + line |= Constants.RESIDUAL_OVERFLOW_FLAG_MASK; } if (residualUnderflow) { - line |= Constants.READ_RESIDUAL_UNDERFLOW_FLAG_MASK; + line |= Constants.RESIDUAL_UNDERFLOW_FLAG_MASK; } if (statusFlag) { line |= STATUS_FLAG_MASK; From 0c1f7b3d38d442a3ec323fe7b6abe07790006940 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 5 Aug 2022 10:18:58 +0800 Subject: [PATCH 09/21] Fix not to remove shared target info --- .../java/org/jscsi/target/TargetServer.java | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java index 9bd1918ff..d41af596a 100644 --- a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java +++ b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java @@ -40,7 +40,7 @@ /** * The central class of the jSCSI Target, which keeps track of all active {@link TargetSession}s, stores target-wide * parameters and variables, and which contains the {@link #main(String[])} method for starting the program. - * + * * @author Andreas Ergenzinger, University of Konstanz * @author Sebastian Graf, University of Konstanz */ @@ -64,7 +64,7 @@ public class TargetServer implements Callable { private Configuration config; /** - * + * */ private DeviceIdentificationVpdPage deviceIdentificationVpdPage; @@ -114,7 +114,7 @@ public TargetServer (final Configuration conf) { /** * Gets and increments the value to use in the next unreserved Target Transfer Tag field of the next * PDU to be sent by the jSCSI Target. - * + * * @see #nextTargetTransferTag * @return the value to use in the next unreserved Target Transfer Tag * field @@ -130,7 +130,7 @@ public static int getNextTargetTransferTag () { /** * Starts the jSCSI target. - * + * * @param args all command line arguments are ignored * @throws IOException */ @@ -175,8 +175,8 @@ public static void main (String[] args) throws Exception { String targetAddress = addresses.get(chosenIndex).getHostAddress(); System.out.println("Using ip address " + addresses.get(chosenIndex).getHostAddress()); - - + + switch (args.length) { case 0 : target = new TargetServer(Configuration.create(targetAddress)); @@ -217,6 +217,9 @@ public Void call() throws Exception { } finally { // coming back from call() means the session is ended // we can delete the target from local cache. + // - - - - + // No, it is not local cache, but shared by all sessions. + /* synchronized (targets) { Target target = targetConnection.getTargetSession().getTarget(); if (target != null) { @@ -231,11 +234,13 @@ public Void call() throws Exception { LOGGER.warn("No target to delete on logout?"); } } + */ } return null; } } + @Override public Void call () throws Exception { // Create a blocking server socket and check for connections @@ -263,7 +268,7 @@ public Void call () throws Exception { // confirm OpCode- if (pdu.getBasicHeaderSegment().getOpCode() != OperationCode.LOGIN_REQUEST) throw new InternetSCSIException(); // get initiatorSessionID - + LoginRequestParser parser = (LoginRequestParser) pdu.getBasicHeaderSegment().getParser(); ISID initiatorSessionID = parser.getInitiatorSessionID(); @@ -319,7 +324,7 @@ public Target getTarget (String targetName) { /** * Removes a session from the jSCSI Target's list of active sessions. - * + * * @param session the session to remove from the list of active sessions */ public synchronized void removeTargetSession (TargetSession session) { @@ -334,14 +339,14 @@ public String[] getTargetNames () { /** * Checks to see if this target name is valid. - * + * * @param checkTargetName * @return true if the the target name is configured */ public boolean isValidTargetName (String checkTargetName) { return targets.containsKey(checkTargetName); } - + /** * Stop this target server */ From 18de931efbc97c1c47ec11bd22b62d73ce3ecb06 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 5 Aug 2022 10:52:56 +0800 Subject: [PATCH 10/21] Linux CentOS 7 bundled iSCSI target support Block Limits and Logical Block Provisioning VPD pages. So do I. --- .../stage/fullfeature/InquiryStage.java | 34 +++++++++++++++++++ .../jscsi/target/scsi/inquiry/PageCode.java | 20 ++++++++--- .../scsi/inquiry/SupportedVpdPages.java | 15 +++++--- 3 files changed, 59 insertions(+), 10 deletions(-) diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java index 068fe41c1..c93e797bb 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java @@ -2,7 +2,9 @@ import java.io.IOException; +import java.nio.ByteBuffer; import java.security.DigestException; +import java.util.Arrays; import org.jscsi.exception.InternetSCSIException; import org.jscsi.parser.BasicHeaderSegment; @@ -26,6 +28,7 @@ * A stage for processing INQUIRY SCSI commands. * * @author Andreas Ergenzinger + * @author CHEN Qingcan */ public class InquiryStage extends TargetFullFeatureStage { @@ -97,6 +100,19 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep case UNIT_SERIAL_NUMBER : responseData = UnitSerialNumberPage.getInstance(); break; + case BLOCK_LIMITS : + byte[] responseBlockLimits = new byte [4 + 0x3c]; + Arrays.fill (responseBlockLimits, (byte)0); // nothing special, all zero. + responseBlockLimits[1] = (byte) 0xb0; // Block Limits + responseBlockLimits[3] = (byte) 0x3c; // PAGE LENGTH + responseData = new SimplePage (responseBlockLimits); + break; + case LOGICAL_BLOCK_PROVISIONING : + responseData = new SimplePage (new byte[] { + 0x00, (byte) 0xb2, 0x00, 0x04, // PAGE LENGTH = 4 + 0x00, 0x00, 0x00, 0x00 // nothing special, all zero. + }); + break; default : // The initiator must not request unsupported mode pages. throw new InternetSCSIException(); @@ -105,7 +121,25 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep // send response sendResponse(bhs.getInitiatorTaskTag(), parser.getExpectedDataTransferLength(), responseData); + } + } + + private static class SimplePage implements IResponseData { + + private final byte[] data; + public SimplePage (byte[] data) { + this.data = data != null ? data : new byte [0]; + } + + @Override + public void serialize (ByteBuffer out, int index) { + out.position (index); + out.put (data); + } + @Override + public int size () { + return data.length; } } diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/PageCode.java b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/PageCode.java index 10498e3b1..fe1308bce 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/PageCode.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/PageCode.java @@ -11,7 +11,7 @@ * To find out which page was requested, first create a new {@link PageCode} object using the value provided in the * command descriptor block's PAGE CODE field, and then call its {@link #getVitalProductDataPageName()} method. This * complicated approach is necessary, since some VPD pages are associated with more than just one page code. - * + * * @author Andreas Ergenzinger */ public class PageCode { @@ -23,7 +23,7 @@ public class PageCode { /** * Creates a new {@link PageCode} object. - * + * * @param value the value of the PAGE CODE field */ public PageCode (final byte value) { @@ -32,7 +32,7 @@ public PageCode (final byte value) { /** * Returns the value of the PAGE CODE field. - * + * * @return the value of the PAGE CODE field */ public final byte getValue () { @@ -41,7 +41,7 @@ public final byte getValue () { /** * Returns the VPD page name associated with the PAGE CODE {@link #value}. - * + * * @return the VPD page name associated with the PAGE CODE {@link #value} */ public final VitalProductDataPageName getVitalProductDataPageName () { @@ -55,6 +55,8 @@ public final VitalProductDataPageName getVitalProductDataPageName () { if (value == 0x86) return VitalProductDataPageName.EXTENDED_INQUIRY_DATA; if (value == 0x87) return VitalProductDataPageName.MODE_PAGE_POLICY; if (value == 0x88) return VitalProductDataPageName.SCSI_PORTS; + if (value == 0xb0) return VitalProductDataPageName.BLOCK_LIMITS; + if (value == 0xb2) return VitalProductDataPageName.LOGICAL_BLOCK_PROVISIONING; if (0x89 <= value && value <= 0xaf) return VitalProductDataPageName.RESERVED; if (0xb0 <= value && value <= 0xbf) return VitalProductDataPageName.DEVICE_TYPE_SPECIFIC; @@ -64,7 +66,7 @@ public final VitalProductDataPageName getVitalProductDataPageName () { /** * An enumeration of unique identifiers for Vital Product Data Pages. - * + * * @author Andreas Ergenzinger */ public enum VitalProductDataPageName { @@ -112,6 +114,14 @@ public enum VitalProductDataPageName { * {@link PageCode} values 0x89-0xaf */ RESERVED, + /** + * {@link PageCode} value 0xb0 + */ + BLOCK_LIMITS, + /** + * {@link PageCode} value 0xb2 + */ + LOGICAL_BLOCK_PROVISIONING, /** * {@link PageCode} values 0xb0-0xbf */ diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java index 4069b2dcf..ed3065af6 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/SupportedVpdPages.java @@ -13,6 +13,7 @@ * This class uses the singleton pattern since the list of supported Vital Product Data page requests will never change. * * @author Andreas Ergenzinger + * @author CHEN Qingcan */ public final class SupportedVpdPages implements IResponseData { @@ -31,8 +32,13 @@ public final class SupportedVpdPages implements IResponseData { */ public static final byte[] SUPPORTED_VPD_PAGES = new byte[] { (byte) 0x00,// SUPPORTED_VPD_PAGES, mandatory - (byte) 0x80,// UNIT_SERIAL_NUMBER + (byte) 0x80,// UNIT_SERIAL_NUMBER, parted bundled in Linux CentOS 7 fires this + // without inquiry SUPPORTED_VPD_PAGES first. (byte) 0x83,// DECIVE_IDENTIFICATION, mandatory + (byte) 0xb0,// BLOCK_LIMITS + (byte) 0xb2,// LOGICAL_BLOCK_PROVISIONING + // Linux CentOS 7 bundled iSCSI target support these two pages. + // So do I. }; /** @@ -75,13 +81,12 @@ public void serialize (ByteBuffer byteBuffer, int index) { // *** byte 3 *** /* - * Page Length: n - 3 = 5 - 3 = 2 (for now) + * Page Length: n - 3 */ byteBuffer.put((byte) SUPPORTED_VPD_PAGES.length); - // *** bytes 4 and 5 - Supported VPD Pages *** - for (int i = 0; i < SUPPORTED_VPD_PAGES.length; ++i) - byteBuffer.put(SUPPORTED_VPD_PAGES[i]); + // *** bytes 4 and above - Supported VPD Pages *** + byteBuffer.put(SUPPORTED_VPD_PAGES); } @Override From 4f1e6e3a8ddc4108cb86acba38a17d53bae292c6 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 5 Aug 2022 10:53:53 +0800 Subject: [PATCH 11/21] Enrich trace messages --- .../org/jscsi/parser/ProtocolDataUnit.java | 14 +++++- .../parser/login/LoginResponseParser.java | 47 +++++++++---------- 2 files changed, 34 insertions(+), 27 deletions(-) diff --git a/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java b/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java index 935404735..0e50c662b 100644 --- a/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java +++ b/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java @@ -25,6 +25,7 @@ import java.security.DigestException; import java.util.AbstractList; import java.util.ArrayList; +import java.util.Arrays; import java.util.Iterator; import com.google.common.io.BaseEncoding; @@ -518,6 +519,17 @@ public final void setDataDigest (final IDigest newDataDigest) { @Override public final String toString () { final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE); + BaseEncoding base16 = BaseEncoding.base16 ().withSeparator (" ", 2).lowerCase (); + + try { + sb.append ("First 16 bytes: 0x"); + byte[] pdu = new byte [calcSize()]; + basicHeaderSegment.serialize (ByteBuffer.wrap (pdu), 0); + sb.append (base16.encode (pdu, 0, 16)).append ("\n"); + + } catch (InternetSCSIException e) { + sb.append ("Exception in serialize: " + e.getMessage ()); + } sb.append(basicHeaderSegment.toString()); for (AdditionalHeaderSegment ahs : additionalHeaderSegments) { @@ -531,7 +543,7 @@ public final String toString () { // "If the mark is defined and larger than the new position then it is discarded." int pos = dataSegment.position (); dataSegment.position (0).get (preview).position (pos); - sb.append (BaseEncoding.base16 ().withSeparator (" ", 2).encode (preview)); + sb.append (base16.encode (preview)); } return sb.toString(); diff --git a/bundles/commons/src/main/java/org/jscsi/parser/login/LoginResponseParser.java b/bundles/commons/src/main/java/org/jscsi/parser/login/LoginResponseParser.java index b2814718b..3d3d8a719 100644 --- a/bundles/commons/src/main/java/org/jscsi/parser/login/LoginResponseParser.java +++ b/bundles/commons/src/main/java/org/jscsi/parser/login/LoginResponseParser.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -25,6 +25,8 @@ import org.jscsi.parser.TargetMessageParser; import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat; import org.jscsi.utils.Utils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** @@ -59,11 +61,13 @@ * negotiation keys). All keys in Chapter 12, except for the X extension formats, MUST be supported by iSCSI initiators * and targets. Keys in Chapter 11, only need to be supported when the function to which they refer is mandatory to * implement. - * + * * @author Volker Wildi */ public final class LoginResponseParser extends TargetMessageParser { + private static final Logger LOGGER = LoggerFactory.getLogger (LoginResponseParser.class); + // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- @@ -87,7 +91,7 @@ public final class LoginResponseParser extends TargetMessageParser { *

* If the target wishes to reject the Login Request for more than one reason, it should return the primary reason * for the rejection. - * + * * @see LoginStatus */ private LoginStatus status; @@ -115,7 +119,7 @@ public final class LoginResponseParser extends TargetMessageParser { /** * Default constructor, creates a new, empty LoginResponseParser object. - * + * * @param initProtocolDataUnit The reference ProtocolDataUnit instance, which contains this * LoginResponseParser subclass object. */ @@ -195,7 +199,7 @@ public final boolean canHaveDigests () { * A non-zero Status-Class indicates an exception. In this case, Status-Class is sufficient for a simple initiator * to use when handling exceptions, without having to look at the Status-Detail. The Status-Detail allows * finer-grained exception handling for more sophisticated initiators and for better information for logging. - * + * * @return The status of this LoginResponseParser object. */ public final LoginStatus getStatus () { @@ -208,7 +212,7 @@ public final LoginStatus getStatus () { * complete (it will be continued on subsequent Login Responses); otherwise, it indicates that this Login Response * ends a set of key=value pairs. A Login Response with the C bit set to 1 MUST have the * T bit set to 0. - * + * * @return The status of the Continue Flag of this LoginResponseParser object. */ public final boolean isContinueFlag () { @@ -218,7 +222,7 @@ public final boolean isContinueFlag () { /** * Returns the Current Stage Number of this Login Response Message. - * + * * @return Number of the Current Stage. * @see org.jscsi.parser.login.LoginStage */ @@ -229,7 +233,7 @@ public final LoginStage getCurrentStageNumber () { /** * Returns the Initiator Session ID (ISID) of this LoginResponseParser object. - * + * * @return Returns the Initiator Session ID (ISID) of this LoginResponseParser object. * @see ISID */ @@ -244,7 +248,7 @@ public final ISID getInitiatorSessionID () { * All Login Responses within the Login Phase MUST carry the same Version-max. *

* The initiator MUST use the value presented as a response to the first Login Request. - * + * * @return The maximum version of this login request message. */ public final int getMaxVersion () { @@ -261,7 +265,7 @@ public final int getMaxVersion () { * All Login Responses within the Login Phase MUST carry the same Version-active. *

* The initiator MUST use the value presented as a response to the first Login Request. - * + * * @return The active version of this LoginResponseParser object. */ public final int getActiveVersion () { @@ -271,7 +275,7 @@ public final int getActiveVersion () { /** * Returns the Next Stage Number of this Login Response Message. - * + * * @return The Number of the Next Stage. * @see org.jscsi.parser.login.LoginStage */ @@ -286,7 +290,7 @@ public final LoginStage getNextStageNumber () { * Final-Response in a new session, this field should be set to the TSIH provided by the initiator in the Login * Request. For a new session, the target MUST generate a non-zero TSIH and ONLY return it in the Login * Final-Response (see Section 5.3 Login Phase). - * + * * @return Returns the Target Session Identifying Handle of this LoginResponseParser object. */ public final short getTargetSessionIdentifyingHandle () { @@ -361,19 +365,10 @@ protected final void deserializeBytes36to39 (final int line) throws InternetSCSI /** {@inheritDoc} */ @Override protected final void checkIntegrity () throws InternetSCSIException { - - String exceptionMessage; - do { - if (status != LoginStatus.SUCCESS && statusSequenceNumber != 0) { - exceptionMessage = "While no successful login is preformed, the StatusSequenceNumber must be 0."; - break; - } - - // message is checked correctly - return; - } while (false); - - throw new InternetSCSIException(exceptionMessage); + if (status != LoginStatus.SUCCESS && statusSequenceNumber != 0) { + LOGGER.warn ("While no successful login is preformed, the StatusSequenceNumber must be 0."); + // throw new InternetSCSIException(exceptionMessage); + } } // -------------------------------------------------------------------------- From a4a615069aca3597e01bd17c78b10e532c8259ab Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 5 Aug 2022 14:25:35 +0800 Subject: [PATCH 12/21] Determine whether supported by requested operation code --- .../org/jscsi/parser/ProtocolDataUnit.java | 3 +-- .../stage/fullfeature/ReportOpCodesStage.java | 8 +++++++- .../org/jscsi/target/scsi/cdb/OpCodesCDB.java | 20 ++++++++++++++++--- .../jscsi/target/scsi/report/OneOpCode.java | 17 ++++++++++------ 4 files changed, 36 insertions(+), 12 deletions(-) diff --git a/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java b/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java index 0e50c662b..8ee506888 100644 --- a/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java +++ b/bundles/commons/src/main/java/org/jscsi/parser/ProtocolDataUnit.java @@ -25,7 +25,6 @@ import java.security.DigestException; import java.util.AbstractList; import java.util.ArrayList; -import java.util.Arrays; import java.util.Iterator; import com.google.common.io.BaseEncoding; @@ -522,7 +521,7 @@ public final String toString () { BaseEncoding base16 = BaseEncoding.base16 ().withSeparator (" ", 2).lowerCase (); try { - sb.append ("First 16 bytes: 0x"); + sb.append (" First 16 bytes: 0x"); byte[] pdu = new byte [calcSize()]; basicHeaderSegment.serialize (ByteBuffer.wrap (pdu), 0); sb.append (base16.encode (pdu, 0, 16)).append ("\n"); diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java index c441f69f9..3956bf05d 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java @@ -44,13 +44,19 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep LOGGER.debug("cdb.getAllocationLength() = " + cdb.getAllocationLength()); LOGGER.debug("cdb.isNormalACA() = " + cdb.isNormalACA()); LOGGER.debug("cdb.getReportingOptions() = " + cdb.getReportingOptions()); + LOGGER.debug("cdb.getRequestedOperationCode() = " + cdb.getRequestedOperationCode()); + } + + if (cdb.getReportingOptions() != 0b001) { + throw new InternetSCSIException("Only REPORTING OPTIONS = 001b is supported now but request is " + + cdb.getReportingOptions()); } // send response sendResponse ( bhs.getInitiatorTaskTag(), parser.getExpectedDataTransferLength(), - new OneOpCode (cdb.getReportingOptions()) + new OneOpCode (cdb.getRequestedOperationCode()) ); } diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java index 36d1d3cec..17ee619e0 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java @@ -7,19 +7,23 @@ /** - * This class represents Command Descriptor Blocks for the REPORT SUPPORTED OPERATION CODES SCSI command. + * This class represents Command Descriptor Blocks for the REPORT SUPPORTED OPERATION CODES SCSI command. * * @author CHEN Qingcan */ public class OpCodesCDB extends CommandDescriptorBlock { private final int inReportingOptions; + private final int inRequestedOperationCode; + private final int inRequestedServiceAction; private final int inAllocationLength; public OpCodesCDB (ByteBuffer buffer) { super (buffer); - inReportingOptions = ReadWrite.readOneByteInt (buffer, 2) & 7; - inAllocationLength = ReadWrite.readFourByteInt (buffer, 6); + inReportingOptions = ReadWrite.readOneByteInt (buffer, 2) & 7; + inRequestedOperationCode = ReadWrite.readOneByteInt (buffer, 3); + inRequestedServiceAction = ReadWrite.readTwoByteInt (buffer, 4); + inAllocationLength = ReadWrite.readFourByteInt (buffer, 6); } /** REPORTING OPTIONS field */ @@ -27,6 +31,16 @@ public int getReportingOptions () { return inReportingOptions; } + /** REQUESTED OPERATION CODE field */ + public int getRequestedOperationCode () { + return inRequestedOperationCode; + } + + /** REQUESTED SERVICE ACTION field */ + public int getRequestedServiceAction () { + return inRequestedServiceAction; + } + /** ALLOCATION LENGTH field */ public int getAllocationLength () { return inAllocationLength; diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java b/bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java index e1b36131c..958b2a65d 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/report/OneOpCode.java @@ -4,6 +4,7 @@ import java.nio.ByteBuffer; import org.jscsi.target.scsi.IResponseData; +import org.jscsi.target.scsi.cdb.ScsiOperationCode; /** * Supported one_command parameter data format. @@ -12,24 +13,28 @@ */ public final class OneOpCode implements IResponseData { - private final int reqOpCode; + private final byte reqOpCode; + private final boolean isSupported; /** * @param reqOpCode requested operation code */ public OneOpCode (int reqOpCode) { - this.reqOpCode = reqOpCode; - // TODO determine whether supported by reqOpCode + this.reqOpCode = (byte) reqOpCode; + this.isSupported = ScsiOperationCode.valueOf (this.reqOpCode) != null; } @Override public void serialize (ByteBuffer out, int index) { out.position (index); out.put ((byte) 0); - out.put ((byte) 0b011); // The device server supports the requested command in conformance with a SCSI standard. + out.put (isSupported ? + (byte) 0b011 : // The device server supports the requested command in conformance with a SCSI standard. + (byte) 0b000 // Data about the requested SCSI command is not currently available. + ); out.put ((byte) 0); - out.put ((byte) 1); // CDB size - out.put ((byte) reqOpCode); // CDB usage data + out.put ((byte) 1); // CDB size + out.put (reqOpCode); // CDB usage data } @Override From 85a315886aa319c1fdb9f32a542d75a076062d7d Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Fri, 5 Aug 2022 15:39:13 +0800 Subject: [PATCH 13/21] Add SYNCHRONIZE CACHE (10), (16) SCSI commands --- .../phase/TargetFullFeaturePhase.java | 7 +- .../stage/fullfeature/ReportOpCodesStage.java | 7 +- .../stage/fullfeature/SyncCacheStage.java | 67 +++++++++++++++++++ .../org/jscsi/target/scsi/cdb/OpCodesCDB.java | 2 +- .../target/scsi/cdb/ScsiOperationCode.java | 2 + .../jscsi/target/scsi/cdb/SyncCacheCDB.java | 47 +++++++++++++ .../jscsi/target/storage/IStorageModule.java | 33 ++++++--- 7 files changed, 149 insertions(+), 16 deletions(-) create mode 100644 bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/SyncCacheStage.java create mode 100644 bundles/target/src/main/java/org/jscsi/target/scsi/cdb/SyncCacheCDB.java diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java b/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java index a52b3976a..0f1a8fea9 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/phase/TargetFullFeaturePhase.java @@ -23,6 +23,7 @@ import org.jscsi.target.connection.stage.fullfeature.ReportOpCodesStage; import org.jscsi.target.connection.stage.fullfeature.RequestSenseStage; import org.jscsi.target.connection.stage.fullfeature.SendDiagnosticStage; +import org.jscsi.target.connection.stage.fullfeature.SyncCacheStage; import org.jscsi.target.connection.stage.fullfeature.TargetFullFeatureStage; import org.jscsi.target.connection.stage.fullfeature.TestUnitReadyStage; import org.jscsi.target.connection.stage.fullfeature.TextNegotiationStage; @@ -91,7 +92,6 @@ public boolean execute () throws DigestException , IOException , InterruptedExce if (connection.getTargetSession().isNormalSession()) { final SCSICommandParser parser = (SCSICommandParser) bhs.getParser(); ScsiOperationCode scsiOpCode = ScsiOperationCode.valueOf(parser.getCDB().get(0)); - int scsiServiceAction = parser.getCDB().get(1) & 0x1F; LOGGER.debug("scsiOpCode = " + scsiOpCode);// log SCSI // Operation Code @@ -144,12 +144,17 @@ public boolean execute () throws DigestException , IOException , InterruptedExce stage = new ReportLunsStage(this); break; case REPORT_OP_CODES : + int scsiServiceAction = parser.getCDB().get(1) & 0b11111; if (scsiServiceAction == 0x0C) { stage = new ReportOpCodesStage(this); } else { scsiOpCode = null; } break; + case SYNCHRONIZE_CACHE_16: + case SYNCHRONIZE_CACHE_10: + stage = new SyncCacheStage(this); + break; default : scsiOpCode = null; diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java index 3956bf05d..4c0103900 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/ReportOpCodesStage.java @@ -12,7 +12,6 @@ import org.jscsi.target.scsi.cdb.OpCodesCDB; import org.jscsi.target.scsi.report.OneOpCode; import org.jscsi.target.settings.SettingsException; -import org.jscsi.target.util.Debug; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,20 +35,16 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep final SCSICommandParser parser = (SCSICommandParser) bhs.getParser(); // get command details in CDB - if (LOGGER.isDebugEnabled()) {// print CDB bytes - LOGGER.debug("CDB bytes: \n" + Debug.byteBufferToString(parser.getCDB())); - } final OpCodesCDB cdb = new OpCodesCDB(parser.getCDB()); if (LOGGER.isDebugEnabled()) { LOGGER.debug("cdb.getAllocationLength() = " + cdb.getAllocationLength()); - LOGGER.debug("cdb.isNormalACA() = " + cdb.isNormalACA()); LOGGER.debug("cdb.getReportingOptions() = " + cdb.getReportingOptions()); LOGGER.debug("cdb.getRequestedOperationCode() = " + cdb.getRequestedOperationCode()); } if (cdb.getReportingOptions() != 0b001) { throw new InternetSCSIException("Only REPORTING OPTIONS = 001b is supported now but request is " + - cdb.getReportingOptions()); + Integer.toBinaryString (cdb.getReportingOptions()) + "b."); } // send response diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/SyncCacheStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/SyncCacheStage.java new file mode 100644 index 000000000..5196613d7 --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/SyncCacheStage.java @@ -0,0 +1,67 @@ +package org.jscsi.target.connection.stage.fullfeature; + +import java.io.IOException; +import java.security.DigestException; + +import org.jscsi.exception.InternetSCSIException; +import org.jscsi.parser.BasicHeaderSegment; +import org.jscsi.parser.ProtocolDataUnit; +import org.jscsi.parser.scsi.SCSICommandParser; +import org.jscsi.parser.scsi.SCSIResponseParser; +import org.jscsi.parser.scsi.SCSIStatus; +import org.jscsi.target.connection.TargetPduFactory; +import org.jscsi.target.connection.phase.TargetFullFeaturePhase; +import org.jscsi.target.scsi.ScsiResponseDataSegment; +import org.jscsi.target.scsi.cdb.SyncCacheCDB; +import org.jscsi.target.settings.SettingsException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + +/** + * A stage for processing SYNCHRONIZE CACHE (10), (16) SCSI commands. + * + * @author CHEN Qingcan + */ +public final class SyncCacheStage extends TargetFullFeatureStage { + + private static final Logger LOGGER = LoggerFactory.getLogger(SyncCacheStage.class); + + public SyncCacheStage(TargetFullFeaturePhase targetFullFeaturePhase) { + super (targetFullFeaturePhase); + } + + @Override + public void execute (ProtocolDataUnit pdu) throws IOException , DigestException , InterruptedException , InternetSCSIException , SettingsException { + // get relevant values from PDU/CDB + BasicHeaderSegment bhs = pdu.getBasicHeaderSegment(); + SCSICommandParser parser = (SCSICommandParser) bhs.getParser(); + final int initiatorTaskTag = bhs.getInitiatorTaskTag(); + + // get command details in CDB + final SyncCacheCDB cdb = new SyncCacheCDB(parser.getCDB()); + if (LOGGER.isDebugEnabled()) { + LOGGER.debug("cdb.getLogicalBlockAddress() = " + cdb.getLogicalBlockAddress()); + LOGGER.debug("cdb.getNumberOfBlocks() = " + cdb.getNumberOfBlocks()); + } + + // synchronize cache + session.getStorageModule().syncCahe (cdb.getLogicalBlockAddress(), cdb.getNumberOfBlocks()); + + // send SCSI Response PDU + pdu = TargetPduFactory.createSCSIResponsePdu( + false,// bidirectionalReadResidualOverflow + false,// bidirectionalReadResidualUnderflow + false,// residualOverflow + false,// residualUnderflow + SCSIResponseParser.ServiceResponse.COMMAND_COMPLETED_AT_TARGET,// response + SCSIStatus.GOOD,// status + initiatorTaskTag, 0,// snackTag + 0,// (ExpDataSN or) Reserved + 0,// bidirectionalReadResidualCount + 0,// residualCount + ScsiResponseDataSegment.EMPTY_DATA_SEGMENT);// dataSegment + connection.sendPdu(pdu); + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java index 17ee619e0..850ae0a36 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/OpCodesCDB.java @@ -20,7 +20,7 @@ public class OpCodesCDB extends CommandDescriptorBlock { public OpCodesCDB (ByteBuffer buffer) { super (buffer); - inReportingOptions = ReadWrite.readOneByteInt (buffer, 2) & 7; + inReportingOptions = ReadWrite.readOneByteInt (buffer, 2) & 0b111; inRequestedOperationCode = ReadWrite.readOneByteInt (buffer, 3); inRequestedServiceAction = ReadWrite.readTwoByteInt (buffer, 4); inAllocationLength = ReadWrite.readFourByteInt (buffer, 6); diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java index 6cd7a83f0..31f58e09a 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/ScsiOperationCode.java @@ -26,8 +26,10 @@ public enum ScsiOperationCode { READ_CAPACITY_10 ((byte) 0x25), READ_10 ((byte) 0x28), WRITE_10 ((byte) 0x2a), + SYNCHRONIZE_CACHE_10((byte) 0x35), READ_16 ((byte) 0x88), WRITE_16 ((byte) 0x8a), + SYNCHRONIZE_CACHE_16((byte) 0x91), READ_CAPACITY_16 ((byte) 0x9e), REPORT_LUNS ((byte) 0xa0), /** REPORT SUPPORTED OPERATION CODES */ diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/SyncCacheCDB.java b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/SyncCacheCDB.java new file mode 100644 index 000000000..ee6053eb2 --- /dev/null +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/cdb/SyncCacheCDB.java @@ -0,0 +1,47 @@ +package org.jscsi.target.scsi.cdb; + + +import java.nio.ByteBuffer; + +import org.jscsi.exception.InternetSCSIException; +import org.jscsi.target.util.ReadWrite; + + +/** + * This class represents Command Descriptor Blocks for the SYNCHRONIZE CACHE (10), (16) SCSI command. + * + * @author CHEN Qingcan + */ +public class SyncCacheCDB extends CommandDescriptorBlock { + + private final byte inOperationCode; + private final long inLogicalBlockAddress; + private final int inNumberOfBlocks; + + public SyncCacheCDB (ByteBuffer buffer) + throws InternetSCSIException { + super (buffer); + inOperationCode = (byte) ReadWrite.readOneByteInt (buffer, 0); + if (inOperationCode == ScsiOperationCode.SYNCHRONIZE_CACHE_16.value ()) { + inLogicalBlockAddress = ReadWrite.readUnsignedInt (buffer, 2); + inNumberOfBlocks = ReadWrite.readTwoByteInt (buffer, 7); + } else if (inOperationCode == ScsiOperationCode.SYNCHRONIZE_CACHE_10.value ()) { + inLogicalBlockAddress = ReadWrite.readEightByteInt (buffer, 2); + inNumberOfBlocks = ReadWrite.readFourByteInt (buffer, 10); + } else { + throw new InternetSCSIException("SYNCHRONIZE_CACHE only supports (10) and (16) but request is " + + Integer.toHexString (inOperationCode) + "h."); + } + } + + /** LOGICAL BLOCK ADDRESS field */ + public long getLogicalBlockAddress () { + return inLogicalBlockAddress; + } + + /** NUMBER OF BLOCKS field */ + public int getNumberOfBlocks () { + return inNumberOfBlocks; + } + +} diff --git a/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java b/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java index 115904450..430772bb4 100644 --- a/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java +++ b/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java @@ -1,9 +1,10 @@ /** - * + * */ package org.jscsi.target.storage; +import java.io.Closeable; import java.io.IOException; import org.jscsi.target.scsi.cdb.CommandDescriptorBlock; @@ -15,10 +16,11 @@ *

* All index and length parameters used by the read and write methods are referring to bytes, unlike the values sent in * {@link CommandDescriptorBlock} s. - * + * * @author Andreas Ergenzinger + * @author CHEN Qingcan */ -public interface IStorageModule { +public interface IStorageModule extends Closeable { /** * This method can be used for checking if a (series of) I/O operations will result in an {@link IOException} due to * trying to access blocks outside the medium's boundaries. @@ -51,7 +53,7 @@ public interface IStorageModule { * *

* Note that the parameters of this method are referring to blocks, not to byte indices. - * + * * @param logicalBlockAddress the index of the first block of data to be read or written * @param transferLengthInBlocks the total number of consecutive blocks about to be read or written * @return see table in description @@ -60,14 +62,14 @@ public interface IStorageModule { /** * Returns the storage space size in bytes divided by the block size in bytes (rounded down). - * + * * @return the virtual amount of storage blocks available */ long getSizeInBlocks (); /** * Copies bytes from storage to the passed byte array. - * + * * @param bytes the array into which the data will be copied will be filled with data from storage * @param storageIndex the position of the first byte to be copied * @throws IOException @@ -76,7 +78,7 @@ public interface IStorageModule { /** * Saves part of the passed byte array's content. - * + * * @param bytes the source of the data to be stored * @param storageIndex byte offset in the storage area * @throws IOException @@ -85,9 +87,10 @@ public interface IStorageModule { /** * Closing the storage. - * + * * @throws IOException to be closed */ + @Override void close () throws IOException; /** @@ -96,4 +99,18 @@ public interface IStorageModule { * @return block size. */ int getBlockSize(); + + /** + * Requests that the implements ensure that the specified logical blocks + * have their most recent data values recorded in non-volatile cache and/or on the medium. + *

+ * Implements may keep the default empty method if no synchronization is needed. + * + * @param syncLogicalBlockAddress LOGICAL BLOCK ADDRESS field + * @param syncNumberOfBlocks NUMBER OF BLOCKS field + * @throws IOException + */ + default void syncCahe (long syncLogicalBlockAddress, int syncNumberOfBlocks) + throws IOException {} + } From 138542649089e8855e57aefb264663f4e2cbab9c Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Sat, 6 Aug 2022 00:14:48 +0800 Subject: [PATCH 14/21] Fix compilation warnings --- .../java/org/jscsi/parser/login/ISID.java | 40 +++++++------- .../TaskManagementFunctionResponseParser.java | 17 +++--- .../jscsi/utils/SerialArithmeticNumber.java | 18 +++---- .../org/jscsi/initiator/Configuration.java | 54 +++++++++---------- .../java/org/jscsi/target/TargetServer.java | 3 +- .../target/storage/JCloudsStorageModule.java | 28 +++++----- 6 files changed, 79 insertions(+), 81 deletions(-) diff --git a/bundles/commons/src/main/java/org/jscsi/parser/login/ISID.java b/bundles/commons/src/main/java/org/jscsi/parser/login/ISID.java index aaa5b6372..9aeb11416 100644 --- a/bundles/commons/src/main/java/org/jscsi/parser/login/ISID.java +++ b/bundles/commons/src/main/java/org/jscsi/parser/login/ISID.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -147,7 +147,7 @@ * it is installed (see Section 9.1.1 Conservative Reuse of ISIDs and Section 9.1.2 iSCSI Name, ISID, and TPGT Use). The * resultant ISID MUST also be persistent over power cycles, reboot, card swap, etc. For details have a look in the * [RFC3721]. - * + * * @author Volker Wildi */ public final class ISID { @@ -186,7 +186,7 @@ private Format (final byte newValue) { /** * Returns the value of this enumeration. - * + * * @return The value of this enumeration. */ public final byte value () { @@ -196,7 +196,7 @@ public final byte value () { /** * Returns the constant defined for the given value. - * + * * @param value The value to search for. * @return The constant defined for the given value. Or null, if this value is not * defined by this enumeration. @@ -209,6 +209,7 @@ public static final Format valueOf (final byte value) { } /** Bit mask to extract the first int out from a long. */ + @SuppressWarnings("unused") private static final long FIRST_LINE_FLAG_MASK = 0xFFFFFFFF00000000L; /** Bit flag mask to get the field A in this ISID. */ @@ -247,7 +248,7 @@ public ISID () { /** * This constructor creates a new ISID object with the given settings. - * + * * @param initT The new T-Value. * @param initA The new A-Value. * @param initB The new B-Value. @@ -266,7 +267,7 @@ public ISID (final Format initT, final byte initA, final short initB, final byte /** * This method creates an Initiator Session ID of the Random format defined in the iSCSI Standard * (RFC3720). - * + * * @param seed The initialization seed for random generator. * @return A instance of an ISID. */ @@ -285,7 +286,7 @@ public static final ISID createRandom (final long seed) { /** * Serializes this ISID object ot its byte representation. - * + * * @return The byte representation of this ISID object. * @throws InternetSCSIException If any violation of the iSCSI-Standard emerge. */ @@ -300,7 +301,7 @@ public final long serialize () throws InternetSCSIException { firstLine |= a << Constants.THREE_BYTES_SHIFT; firstLine &= 0x00ffffff; firstLine |= t.value() << T_FIELD_SHIFT; - + isid = Utils.getUnsignedLong(firstLine) << Constants.FOUR_BYTES_SHIFT; isid |= Utils.getUnsignedLong(d) << Constants.TWO_BYTES_SHIFT; @@ -309,13 +310,13 @@ public final long serialize () throws InternetSCSIException { /** * Parses a given ISID in this ISID obejct. - * + * * @param isid The byte representation of a ISID to parse. * @throws InternetSCSIException If any violation of the iSCSI-Standard emerge. */ final void deserialize ( long isid) throws InternetSCSIException { int line = (int) (isid >>> Constants.FOUR_BYTES_SHIFT); - + t = Format.valueOf((byte) (line >>> T_FIELD_SHIFT)); a = (byte) ((line & A_FIELD_FLAG_MASK) >>> Constants.THREE_BYTES_SHIFT); b = (short) ((line & Constants.MIDDLE_TWO_BYTES_SHIFT) >>> Constants.ONE_BYTE_SHIFT); @@ -332,9 +333,10 @@ final void deserialize ( long isid) throws InternetSCSIException { /** * Creates a string with all fields of this ISID object. - * + * * @return The string representation. */ + @Override public final String toString () { final StringBuilder sb = new StringBuilder(Constants.LOG_INITIAL_SIZE); @@ -351,7 +353,7 @@ public final String toString () { /** * This method compares a given ISID object with this object for value equality. - * + * * @param isid The given ISID object to check. * @return True, if the values of the two ISID objects are equal. Else false. */ @@ -408,7 +410,7 @@ public final void clear () { /** * Returns the value of the field A. - * + * * @return The value of the field A. */ public final byte getA () { @@ -418,7 +420,7 @@ public final byte getA () { /** * Returns the value of the field B. - * + * * @return The value of the field B. */ public final short getB () { @@ -428,7 +430,7 @@ public final short getB () { /** * Returns the value of the field C. - * + * * @return The value of the field C. */ public final byte getC () { @@ -438,7 +440,7 @@ public final byte getC () { /** * Returns the value of the field D. - * + * * @return The value of the field D. */ public final short getD () { @@ -448,7 +450,7 @@ public final short getD () { /** * Returns the value of the field T. - * + * * @return The value of the field T. */ public final Format getT () { @@ -461,7 +463,7 @@ public final Format getT () { /** * This method checks, if all fields are valid. In these cases an exception will be thrown. - * + * * @throws InternetSCSIException If the integrity is violated. */ protected final void checkIntegrity () throws InternetSCSIException { diff --git a/bundles/commons/src/main/java/org/jscsi/parser/tmf/TaskManagementFunctionResponseParser.java b/bundles/commons/src/main/java/org/jscsi/parser/tmf/TaskManagementFunctionResponseParser.java index c8507ad38..3a031d9ff 100644 --- a/bundles/commons/src/main/java/org/jscsi/parser/tmf/TaskManagementFunctionResponseParser.java +++ b/bundles/commons/src/main/java/org/jscsi/parser/tmf/TaskManagementFunctionResponseParser.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -28,7 +28,6 @@ import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.parser.TargetMessageParser; import org.jscsi.parser.datasegment.DataSegmentFactory.DataSegmentFormat; -import org.jscsi.parser.logout.LogoutResponse; import org.jscsi.utils.Utils; @@ -63,14 +62,14 @@ *

*

TotalAHSLength and DataSegmentLength

For this PDU TotalAHSLength and DataSegmentLength MUST be * 0. - * + * * @author Volker Wildi */ public final class TaskManagementFunctionResponseParser extends TargetMessageParser { /** * This enumeration defines all valid response code, which are defined in the iSCSI Standard (RFC 3720). - * + * * @author Volker Wildi */ public static enum ResponseCode { @@ -110,7 +109,7 @@ private ResponseCode (final byte newValue) { /** * Returns the value of this enumeration. - * + * * @return The value of this enumeration. */ public final byte value () { @@ -120,7 +119,7 @@ public final byte value () { /** * Returns the constant defined for the given value. - * + * * @param value The value to search for. * @return The constant defined for the given value. Or null, if this value is not * defined by this enumeration. @@ -142,7 +141,7 @@ public static final ResponseCode valueOf (final byte value) { /** * Default constructor, creates a new, empty TaskManagementFunctionResponseParser object. - * + * * @param initProtocolDataUnit The reference ProtocolDataUnit instance, which contains this * TaskManagementFunctionResponseParser subclass object. */ @@ -260,7 +259,7 @@ public final void clear () { * field in the Task Management function request is outside the valid CmdSN window, then targets must return the * "Task does not exist" response. * - * + * * @return The response code of this TaskManagementFunctionResponseParser object. */ public final ResponseCode getResponse () { diff --git a/bundles/commons/src/main/java/org/jscsi/utils/SerialArithmeticNumber.java b/bundles/commons/src/main/java/org/jscsi/utils/SerialArithmeticNumber.java index 1fc67bea6..25b900ca8 100644 --- a/bundles/commons/src/main/java/org/jscsi/utils/SerialArithmeticNumber.java +++ b/bundles/commons/src/main/java/org/jscsi/utils/SerialArithmeticNumber.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -23,7 +23,7 @@ *

* This class encapsulate the behavior of how to compare and increment a number in Serial Number Arithmetic, which is * defined in [RFC1982]. - * + * * @author Volker Wildi, University of Konstanz */ public final class SerialArithmeticNumber implements Comparable { @@ -57,7 +57,7 @@ public SerialArithmeticNumber () { /** * Constructor to create a new SerialArithmeticNumber instance, which is initialized to * startValue. - * + * * @param startValue The start value. */ public SerialArithmeticNumber (final int startValue) { @@ -72,17 +72,17 @@ public SerialArithmeticNumber (final int startValue) { /** * Same as the compareTo method only with the exception of another parameter type. - * + * * @param anotherSerialNumber The number to compare with. * @return a negative integer, zero, or a positive integer as this SerialArithmeticNumber is less than, * equal to, or greater than the given number. */ public final int compareTo (final int anotherSerialNumber) { - - return compareTo(new Integer(anotherSerialNumber)); + return compareTo(anotherSerialNumber); } /** {@inheritDoc} */ + @Override public final synchronized int compareTo (final Integer anotherSerialNumber) { long diff = serialNumber - Utils.getUnsignedLong(anotherSerialNumber.intValue()); @@ -99,7 +99,7 @@ public final synchronized int compareTo (final Integer anotherSerialNumber) { /** * Returns the current value of this SerialArithmeticNumber instance. - * + * * @return The current value of this SerialArithmeticNumber instance. */ public final synchronized int getValue () { @@ -121,7 +121,7 @@ public final synchronized void increment () { /** * Updates the value of this SerialArithmeticNumber instance to the given one. - * + * * @param newValue The new value. */ public final synchronized void setValue (final int newValue) { diff --git a/bundles/initiator/src/main/java/org/jscsi/initiator/Configuration.java b/bundles/initiator/src/main/java/org/jscsi/initiator/Configuration.java index 6f608afb4..fa6abb4a5 100644 --- a/bundles/initiator/src/main/java/org/jscsi/initiator/Configuration.java +++ b/bundles/initiator/src/main/java/org/jscsi/initiator/Configuration.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -61,7 +61,7 @@ *

* This class stores all informations, which are set during an iSCSI Session, Connection or are set as the default * values. Therefore, this class was implemented as a Singleton Pattern. - * + * * @author Volker Wildi, University of Konstanz */ public final class Configuration { @@ -164,7 +164,7 @@ public Configuration (final Hashtable paramGl /** * Creates a instance of a Configuration object, which is initialized with the settings from the * system-wide configuration file. - * + * * @return A Configuration instance with all settings. * @throws ConfigurationException If this operation is supported but failed for some reason. */ @@ -176,12 +176,12 @@ public static final Configuration create () throws ConfigurationException { /** * Creates a instance of a Configuration object, which is initialized with the settings from the * system-wide configuration file. - * + * * @param configSchemaFileName The file name of the schema to check the configuration file against.s * @param configFileName The file name of the configuration file to use. * @return A Configuration instance with all settings. * @throws ConfigurationException If this operation is supported but failed for some reason. - * + * */ public static final Configuration create (final File configSchemaFileName, final File configFileName) throws ConfigurationException { @@ -195,7 +195,7 @@ public static final Configuration create (final File configSchemaFileName, final /** * Returns the value of a single parameter, instead of all values. - * + * * @param targetName Name of the iSCSI Target to connect. * @param connectionID The ID of the connection to retrieve. * @param textKey The name of the parameter. @@ -225,10 +225,7 @@ public final String getSetting (final String targetName, final int connectionID, final SettingEntry se; synchronized (globalConfig) { se = globalConfig.get(textKey); - - synchronized (se) { - if (se != null) { return se.getValue(); } - } + if (se != null) { return se.getValue(); } } throw new OperationalTextKeyException("No OperationalTextKey entry found for key: " + textKey.value()); @@ -237,7 +234,7 @@ public final String getSetting (final String targetName, final int connectionID, /** * Unifies all parameters (in the right precedence) and returns one SettingsMap. Right order means: * default, then the session-wide, and finally the connection-wide valid parameters. - * + * * @param targetName Name of the iSCSI Target to connect. * @param connectionID The ID of the connection to retrieve. * @return All unified parameters in one single SettingsMap. @@ -273,7 +270,7 @@ public final SettingsMap getSettings (final String targetName, final int connect /** * Returns the value of a single parameter. It can only return session and global parameters. - * + * * @param targetName Name of the iSCSI Target to connect. * @param textKey The name of the parameter. * @return The value of the given parameter. @@ -287,7 +284,7 @@ public final String getSessionSetting (final String targetName, final Operationa /** * Returns the InetAddress instance of the connected iSCSI Target. - * + * * @param targetName The name of the iSCSI Target. * @return The InetAddress instance of the requested iSCSI Target. * @throws NoSuchSessionException if a session with this target name is not open. @@ -303,7 +300,7 @@ public final InetSocketAddress getTargetAddress (final String targetName) throws /** * Updates the stored settings of a connection with these values from the response of the iSCSI Target. - * + * * @param targetName The name of the iSCSI Target. * @param connectionID The ID of the connection within this iSCSI Target. * @param response The response settings. @@ -314,10 +311,9 @@ public final void update (final String targetName, final int connectionID, final final SessionConfiguration sc; synchronized (sessionConfigs) { sc = sessionConfigs.get(targetName); + if (sc == null) { throw new NoSuchSessionException("A session with the ID '" + targetName + "' does not exist."); } synchronized (sc) { - if (sc == null) { throw new NoSuchSessionException("A session with the ID '" + targetName + "' does not exist."); } - synchronized (response) { SettingEntry se; for (Map.Entry e : response.entrySet()) { @@ -350,7 +346,7 @@ public final void update (final String targetName, final int connectionID, final /** * Reads the given configuration file in memory and creates a DOM representation. - * + * * @throws SAXException If this operation is supported but failed for some reason. * @throws ParserConfigurationException If a DocumentBuilder cannot be created which satisfies the * configuration requested. @@ -386,7 +382,7 @@ private final Document parse (final File schemaLocation, final File configFile) /** * Parses all settings form the main configuration file. - * + * * @param root The root element of the configuration. */ private final void parseSettings (final Element root) { @@ -400,7 +396,7 @@ private final void parseSettings (final Element root) { /** * Parses all global settings form the main configuration file. - * + * * @param root The root element of the configuration. */ private final void parseGlobalSettings (final Element root) { @@ -438,7 +434,7 @@ private final void parseGlobalSettings (final Element root) { /** * Parses all target-specific settings form the main configuration file. - * + * * @param root The root element of the configuration. */ private final void parseTargetSpecificSettings (final Element root) { @@ -505,7 +501,7 @@ private final void clear () { /** * This class contains a session-wide SettingsMap with one or more connection-specific * SettingsMap. - * + * * @author Volker Wildi */ private final class SessionConfiguration { @@ -530,7 +526,7 @@ private final class SessionConfiguration { /** * Adds a session-wide parameter to this SessionConfiguration object. - * + * * @param textKey The name of the parameter to add * @param textValue The value of the parameter to add. */ @@ -542,7 +538,7 @@ final void addSessionSetting (final OperationalTextKey textKey, final String tex /** * Updates the value of the given OperationTextKey of this session with the response key * textValue and the result function. - * + * * @param textKey The OperationalTextKey to update. * @param textValue The value of the response. * @param resultFunction The IResultFunction instance to use to obtain the result. @@ -580,7 +576,7 @@ final void updateSessionSetting (final OperationalTextKey textKey, final String /** * Updates the value of the given OperationTextKey of the given connection within this session with * the response key textValue and the result function. - * + * * @param connectionID The ID of the connection. * @param textKey The OperationalTextKey to update. * @param textValue The value of the response. @@ -663,7 +659,7 @@ final void updateConnectionSetting (final int connectionID, final OperationalTex /** * Returns a single setting value of a connection (specified by the ID). - * + * * @param connectionID The ID of the connection. * @param textKey The name of the parameter. * @return the value of the given parameter of the connection. @@ -685,7 +681,7 @@ final String getSetting (final int connectionID, final OperationalTextKey textKe /** * Returns all settings of a connection (specified by the ID). - * + * * @param connectionID The ID of the connection. * @return All session-wide and connection-specific settings of the connection. */ @@ -711,7 +707,7 @@ final SettingsMap getSettings (final int connectionID) { /** * Returns the InetAddress of the leading connection of the session. - * + * * @return An InetAddress instance. */ final InetSocketAddress getInetSocketAddress () { @@ -721,7 +717,7 @@ final InetSocketAddress getInetSocketAddress () { /** * Sets the InetAddress of the leading connection to the given value. - * + * * @param newInetAddress The new InetAddress of the leading connection. * @param port The new Port of the leading connection; * @throws UnknownHostException This exception is thrown, when the host with the given InetAddress diff --git a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java index d41af596a..2d85a69c2 100644 --- a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java +++ b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java @@ -28,7 +28,6 @@ import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.parser.login.ISID; import org.jscsi.parser.login.LoginRequestParser; -import org.jscsi.target.connection.Connection; import org.jscsi.target.connection.Connection.TargetConnection; import org.jscsi.target.connection.TargetSession; import org.jscsi.target.scsi.inquiry.DeviceIdentificationVpdPage; @@ -200,7 +199,7 @@ public static void main (String[] args) throws Exception { target.call(); } - private class ConnectionHandler implements Callable { + private class ConnectionHandler implements Callable { private final TargetConnection targetConnection; diff --git a/bundles/target/src/main/java/org/jscsi/target/storage/JCloudsStorageModule.java b/bundles/target/src/main/java/org/jscsi/target/storage/JCloudsStorageModule.java index 7bd84f3a0..e0a9c812b 100644 --- a/bundles/target/src/main/java/org/jscsi/target/storage/JCloudsStorageModule.java +++ b/bundles/target/src/main/java/org/jscsi/target/storage/JCloudsStorageModule.java @@ -1,5 +1,5 @@ /** - * + * */ package org.jscsi.target.storage; @@ -45,9 +45,9 @@ /** * JClouds-Binding to store blocks as buckets in clouds-backends. This class utilizes caching as well as multithreaded * writing to improve performance. - * + * * @author Sebastian Graf, University of Konstanz - * + * */ @Beta public class JCloudsStorageModule implements IStorageModule { @@ -114,10 +114,10 @@ public class JCloudsStorageModule implements IStorageModule { /** * Creates a new {@link JCloudsStorageModule} backed by the specified file. If no such file exists, a * {@link FileNotFoundException} will be thrown. - * + * * @param pSizeInBlocks blocksize for this module * @param pFile local storage, not used over here - * + * */ public JCloudsStorageModule (final long pSizeInBlocks, final File pFile) { // number * 512 = size in bytes @@ -273,7 +273,7 @@ private final void storeBucket (int pBucketId, byte[] pData) throws InterruptedE /** * {@inheritDoc} - * + * * @throws Exception */ @Override @@ -328,7 +328,7 @@ public int getBlockSize() { /** * Getting credentials for aws from homedir/.credentials - * + * * @return a two-dimensional String[] with login and password */ private static String[] getCredentials () { @@ -353,9 +353,9 @@ private static String[] getCredentials () { /** * Single task to write data to the cloud. - * + * * @author Sebastian Graf, University of Konstanz - * + * */ class ReadTask implements Callable> { @@ -393,7 +393,7 @@ class ReadTask implements Callable> { // + (System.currentTimeMillis() - time) + "\n"); // download.flush(); } else { - try (InputStream is = blob.getPayload().getInput()) { + try (InputStream is = blob.getPayload().openStream()) { data = ByteStreams.toByteArray(is); } // download.write(Integer.toString(mBucketId) + "," + @@ -402,7 +402,7 @@ class ReadTask implements Callable> { // download.flush(); while (data.length < SIZE_PER_BUCKET) { blob = mStore.getBlob(mContainerName, Integer.toString(mBucketId)); - try (InputStream is = blob.getPayload().getInput()) { + try (InputStream is = blob.getPayload().openStream()) { data = ByteStreams.toByteArray(is); } // // DEBUG CODE @@ -444,9 +444,9 @@ public Integer getKey () { /** * Single task to write data to the cloud. - * + * * @author Sebastian Graf, University of Konstanz - * + * */ class WriteTask implements Callable { /** @@ -504,6 +504,7 @@ public Integer call () throws Exception { class ReadFutureCleaner extends Thread { + @Override public void run () { while (true) { try { @@ -520,6 +521,7 @@ public void run () { class WriteFutureCleaner extends Thread { + @Override public void run () { while (true) { try { From 23699fcf9469a259cdb410a2210e51d88642a1d3 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Sat, 6 Aug 2022 01:11:30 +0800 Subject: [PATCH 15/21] Add listening address configuration in XML file --- .../java/org/jscsi/target/Configuration.java | 29 +++++++++++++++++-- .../java/org/jscsi/target/TargetServer.java | 1 + .../src/main/resources/jscsi-target.xml | 1 + .../src/main/resources/jscsi-target.xsd | 2 ++ 4 files changed, 30 insertions(+), 3 deletions(-) diff --git a/bundles/target/src/main/java/org/jscsi/target/Configuration.java b/bundles/target/src/main/java/org/jscsi/target/Configuration.java index baf042f36..73cf77b02 100644 --- a/bundles/target/src/main/java/org/jscsi/target/Configuration.java +++ b/bundles/target/src/main/java/org/jscsi/target/Configuration.java @@ -34,13 +34,15 @@ import org.w3c.dom.Text; import org.xml.sax.SAXException; +import com.google.common.base.Strings; + /** * Instances of {@link Configuration} provides access target-wide parameters, variables that are the same across all * sessions and connections that do not change after initialization and which play a role during text parameter * negotiation. Some of these parameters are provided or can be overridden by the content of an XML file - * jscsi-target.xml. - * + * * @author Andreas Ergenzinger, University of Konstanz */ public class Configuration { @@ -66,6 +68,7 @@ public class Configuration { public static final String ELEMENT_ALLOWSLOPPYNEGOTIATION = "AllowSloppyNegotiation"; public static final String ELEMENT_PORT = "Port"; public static final String ELEMENT_EXTERNAL_PORT = "ExternalPort"; + public static final String ELEMENT_ADDRESS = "Address"; public static final String ELEMENT_EXTERNAL_ADDRESS = "ExternalAddress"; // -------------------------------------------------------------------------- @@ -173,7 +176,7 @@ public Configuration (final String pTargetAddress) throws IOException { } private static String defaultTargetAddress(String pTargetAddress) throws UnknownHostException { - if (pTargetAddress.equals("")) { + if (Strings.isNullOrEmpty (pTargetAddress)) { return InetAddress.getLocalHost().getHostAddress(); } else { @@ -237,7 +240,19 @@ public static Configuration create (final File schemaLocation, final File config * configuration requested. * @throws IOException If any IO errors occur. */ - public static Configuration create (final InputStream schemaLocation, final InputStream configFile, final String pTargetAddress) throws SAXException , ParserConfigurationException , IOException { + public static Configuration create (final File schemaLocation, final File configFile) throws SAXException , ParserConfigurationException , IOException { + return create(new FileInputStream(schemaLocation), new FileInputStream(configFile), null); + } + + /** + * Reads the given configuration file in memory and creates a DOM representation. + * + * @throws SAXException If this operation is supported but failed for some reason. + * @throws ParserConfigurationException If a {@link DocumentBuilder} cannot be created which satisfies the + * configuration requested. + * @throws IOException If any IO errors occur. + */ + public static Configuration create (final InputStream schemaLocation, final InputStream configFile, String pTargetAddress) throws SAXException , ParserConfigurationException , IOException { final SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); final Schema schema = schemaFactory.newSchema(new StreamSource(schemaLocation)); @@ -255,6 +270,14 @@ public static Configuration create (final InputStream schemaLocation, final Inpu validator.validate(source, result); Document root = (Document) result.getNode(); + // target address + if (Strings.isNullOrEmpty (pTargetAddress)) { + NodeList tagsAddress = root.getElementsByTagName(ELEMENT_ADDRESS); + if (tagsAddress.getLength() > 0) { + pTargetAddress = tagsAddress.item(0).getTextContent(); + } + } + // TargetName Configuration returnConfiguration = new Configuration(pTargetAddress); Element targetListNode = (Element) root.getElementsByTagName(ELEMENT_TARGET_LIST).item(0); diff --git a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java index 2d85a69c2..bbd0c94c3 100644 --- a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java +++ b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java @@ -95,6 +95,7 @@ public TargetServer (final Configuration conf) { // read target settings from configuration file + LOGGER.debug(" target address: " + getConfig().getTargetAddress()); LOGGER.debug(" port: " + getConfig().getPort()); LOGGER.debug(" loading targets."); // open the storage medium diff --git a/bundles/target/src/main/resources/jscsi-target.xml b/bundles/target/src/main/resources/jscsi-target.xml index ae890c70e..e8405ace1 100644 --- a/bundles/target/src/main/resources/jscsi-target.xml +++ b/bundles/target/src/main/resources/jscsi-target.xml @@ -52,6 +52,7 @@ true +

0.0.0.0
3260 diff --git a/bundles/target/src/main/resources/jscsi-target.xsd b/bundles/target/src/main/resources/jscsi-target.xsd index 83fc3fef4..7440f62e3 100644 --- a/bundles/target/src/main/resources/jscsi-target.xsd +++ b/bundles/target/src/main/resources/jscsi-target.xsd @@ -90,6 +90,8 @@ + From d7caacf8af4cc72b4ba7ad85baa51734160cdba9 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Mon, 8 Aug 2022 17:54:21 +0800 Subject: [PATCH 16/21] Adds VENDOR, PRODUCT settings in configuration XML, returned returned in Standard INQUIRY data, and adds comments in sample configuration XML file. --- .../src/main/java/org/jscsi/utils/Utils.java | 61 +++++++++---- .../java/org/jscsi/target/Configuration.java | 86 ++++++++++++++++--- .../java/org/jscsi/target/TargetServer.java | 8 +- .../stage/fullfeature/InquiryStage.java | 4 +- .../scsi/inquiry/StandardInquiryData.java | 67 +++++++++------ .../src/main/resources/jscsi-target.xml | 71 ++++++++++++--- .../src/main/resources/jscsi-target.xsd | 10 +++ .../target/connection/ConnectionTest.java | 4 +- 8 files changed, 241 insertions(+), 70 deletions(-) diff --git a/bundles/commons/src/main/java/org/jscsi/utils/Utils.java b/bundles/commons/src/main/java/org/jscsi/utils/Utils.java index ffc8bf6c6..0072579b6 100644 --- a/bundles/commons/src/main/java/org/jscsi/utils/Utils.java +++ b/bundles/commons/src/main/java/org/jscsi/utils/Utils.java @@ -1,13 +1,13 @@ /** * Copyright (c) 2012, University of Konstanz, Distributed Systems Group All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the * following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. * 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. * Neither the name of the University of Konstanz nor the names of its contributors may be used to * endorse or promote products derived from this software without specific prior written permission. - * + * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, @@ -23,12 +23,14 @@ import org.jscsi.exception.InternetSCSIException; +import com.google.common.base.Strings; + /** * This class encapsulate all the needed common constants and methods with are needed by the used classes for the * parsing and logging process. There are also common used bit masks for the extraction of the needed field in such a * iSCSI message. - * + * * @author Volker Wildi, University of Konstanz */ public final class Utils { @@ -65,7 +67,7 @@ public final class Utils { /** * Method to guarantee that a given field is not zero. - * + * * @param field Field to check * @throws InternetSCSIException If the field is not reserved, then throw an exception */ @@ -76,7 +78,7 @@ public static final void isReserved (final long field) throws InternetSCSIExcept /** * Checks for equality of a given value with the expected value. - * + * * @param field This value should be equal to the expected value * @param expected This is what we expect * @throws InternetSCSIException If this comparison failed, this exception will be thrown @@ -88,7 +90,7 @@ public static final void isExpected (final int field, final int expected) throws /** * Checks with a given int is unequal to zero. - * + * * @param num Number to check * @return Returns true if the line unequal than zero */ @@ -102,7 +104,7 @@ public static final boolean isBitSet (final int num) { /** * This methods creates an easy to use interface to print out a logging message of a specific variable. - * + * * @param sb StringBuilder to directly write the logging messages in. * @param fieldName The name of the variable. * @param fieldValue The value of the given variable. @@ -119,7 +121,7 @@ public static final void printField (final StringBuilder sb, final String fieldN /** * This methods creates an easy to use interface to print out a logging message of a specific variable. - * + * * @param sb StringBuilder to directly write the logging messages in. * @param fieldName The name of the variable. * @param fieldValue The value of the given variable. @@ -136,7 +138,7 @@ public static final void printField (final StringBuilder sb, final String fieldN /** * This methods creates an easy to use interface to print out a logging message of a specific variable. - * + * * @param sb StringBuilder to directly write the logging messages in. * @param fieldName The name of the variable. * @param fieldValue The value of the given variable. @@ -153,7 +155,7 @@ public static final void printField (final StringBuilder sb, final String fieldN /** * This methods creates an easy to use interface to print out a logging message of a specific variable. - * + * * @param sb StringBuilder to directly write the logging messages in. * @param fieldName The name of the variable. * @param fieldValue The value of the given variable. @@ -166,7 +168,7 @@ public static final void printField (final StringBuilder sb, final String fieldN /** * This methods creates an easy to use interface to print out a logging message of a specific variable. - * + * * @param sb StringBuilder to directly write the logging messages in. * @param fieldName The name of the variable. * @param fieldValue The value of the given variable. @@ -183,7 +185,7 @@ public static final void printField (final StringBuilder sb, final String fieldN /** * This methods creates an easy to use interface to print out a logging message of a specific variable. - * + * * @param sb StringBuilder to directly write the logging messages in. * @param fieldName The name of the variable. * @param fieldValue The value of the given variable. @@ -206,7 +208,7 @@ public static final void printField (final StringBuilder sb, final String fieldN /** * Appends to a given StringBuilder the given indents depending on the indent level. - * + * * @param sb StringBuilder to write in. * @param indent The number (level) of indents. */ @@ -219,7 +221,7 @@ private static final void indent (final StringBuilder sb, final int indent) { /** * This method converts a byte with the highest (sign) bit set, to an unsigned int value. - * + * * @param b The signed byte number. * @return The unsigned int number. */ @@ -230,7 +232,7 @@ public static final int getUnsignedInt (final byte b) { /** * This method converts an integer with the highest (sign) bit set, to an unsigned long value. - * + * * @param i The signed int number. * @return The unsigned long number. */ @@ -241,7 +243,7 @@ public static final long getUnsignedLong (final int i) { /** * This method converts an short integer with the highest (sign) bit set, to an unsigned long value. - * + * * @param i The signed integer number. * @return The unsigned long number. */ @@ -250,6 +252,33 @@ public static final long getUnsignedLong (final short i) { return i & SHORT_FLAG_MASK_LONG; } + /** + * Returns a string with fixed length. + *

+ * The input string will be truncated if it is shorter than required length, + * or will be padded if it is longer than required length. + * + * @param s Input string. Null is treated as empty string (""). + * @param len Required length. Negative is treated as zero (0). + * @param pad Padding with this character if the input string is shorter than required length. + * Null is treated as space (' '). + * @return Output string with fixed length. + */ + public static final String fixedLengthString (String s, int len, Character pad) { + + s = Strings.nullToEmpty (s); + if (len < 0) len = 0; + if (pad == null) pad = ' '; + + if (s.length () > len) { + return s.substring (0, len); + } else if (s.length () < len) { + return Strings.padEnd (s, len, pad); + } else { // == + return s; + } + } + // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- diff --git a/bundles/target/src/main/java/org/jscsi/target/Configuration.java b/bundles/target/src/main/java/org/jscsi/target/Configuration.java index 73cf77b02..f6edee4e8 100644 --- a/bundles/target/src/main/java/org/jscsi/target/Configuration.java +++ b/bundles/target/src/main/java/org/jscsi/target/Configuration.java @@ -31,7 +31,6 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; -import org.w3c.dom.Text; import org.xml.sax.SAXException; import com.google.common.base.Strings; @@ -44,6 +43,7 @@ * jscsi-target.xml. * * @author Andreas Ergenzinger, University of Konstanz + * @author CHEN Qingcan */ public class Configuration { @@ -61,6 +61,7 @@ public class Configuration { public static final String ELEMENT_ASYNCFILESTORAGE = "AsyncFileStorage"; public static final String ELEMENT_JCLOUDSSTORAGE = "JCloudsStorage"; public static final String ELEMENT_FILESTORAGE = "FileStorage"; + public static final String ELEMENT_PATH= "Path"; public static final String ELEMENT_CREATE = "Create"; public static final String ATTRIBUTE_SIZE = "size"; @@ -71,6 +72,11 @@ public class Configuration { public static final String ELEMENT_ADDRESS = "Address"; public static final String ELEMENT_EXTERNAL_ADDRESS = "ExternalAddress"; + // Vendor info + public static final String ELEMENT_VENDOR_ID = "VendorID"; + public static final String ELEMENT_PRODUCT_ID = "ProductID"; + public static final String ELEMENT_PRODUCT_REVISION = "ProductRevisionLevel"; + // -------------------------------------------------------------------------- // -------------------------------------------------------------------------- @@ -163,6 +169,21 @@ public class Configuration { */ private final int maxRecvTextPduSequenceLength = 4; + /** + * VENDOR IDENTIFICATION field returned in Standard INQUIRY data. + */ + protected String idVendor = ""; + + /** + * PRODUCT IDENTIFICATION field returned in Standard INQUIRY data. + */ + protected String idProduct = ""; + + /** + * PRODUCT REVISION LEVEL field returned in Standard INQUIRY data. + */ + protected String revProduct = ""; + public Configuration(final String pTargetAddress, String externalTargetAddress, int externalPort) throws IOException { this.port = 3260; this.externalPort = externalPort; @@ -325,20 +346,38 @@ public static Configuration create (final InputStream schemaLocation, final Inpu else returnConfiguration.allowSloppyNegotiation = Boolean.parseBoolean(allowSloppyNegotiationNode.getTextContent()); + + // vendor info + NodeList tagsVendorID = root.getElementsByTagName(ELEMENT_VENDOR_ID); + if (tagsVendorID.getLength() > 0) { + returnConfiguration.idVendor = tagsVendorID.item(0).getTextContent(); + } + NodeList tagsProductID = root.getElementsByTagName(ELEMENT_PRODUCT_ID); + if (tagsProductID.getLength() > 0) { + returnConfiguration.idProduct = tagsProductID.item(0).getTextContent(); + } + NodeList tagsProductRevision = root.getElementsByTagName(ELEMENT_PRODUCT_REVISION); + if (tagsProductRevision.getLength() > 0) { + returnConfiguration.revProduct = tagsProductRevision.item(0).getTextContent(); + } + return returnConfiguration; } protected static Target parseTargetElement (Element targetElement) throws IOException { // TargetName - // TargetName - Node nextNode = chopWhiteSpaces(targetElement.getFirstChild()); - // assert - // nextNode.getLocalName().equals(OperationalTextKey.TARGET_NAME); + Node nextNode = chopNotEquals (targetElement.getFirstChild(), TextKeyword.TARGET_NAME); + if (nextNode == null) { + throw new IOException (TextKeyword.TARGET_NAME + " tag not found."); + } String targetName = nextNode.getTextContent(); // TargetAlias (optional) nextNode = chopWhiteSpaces(nextNode.getNextSibling()); + if (nextNode == null) { + throw new IOException (TextKeyword.TARGET_NAME + " tag has no sibling."); + } String targetAlias = ""; if (nextNode.getLocalName().equals(TextKeyword.TARGET_ALIAS)) { targetAlias = nextNode.getTextContent(); @@ -357,12 +396,15 @@ protected static Target parseTargetElement (Element targetElement) throws IOExce case ELEMENT_JCLOUDSSTORAGE : kind = JCloudsStorageModule.class; break; + default: + throw new IOException ("Unknown storage: " + nextNode.getLocalName()); } - // Getting storagepath - nextNode = nextNode.getFirstChild(); - nextNode = chopWhiteSpaces(nextNode); - // assert nextNode.getLocalName().equals(ELEMENT_PATH); + // Getting storage path + nextNode = chopNotEquals (nextNode.getFirstChild(), ELEMENT_PATH); + if (nextNode == null) { + throw new IOException (ELEMENT_PATH + " tag not found."); + } String storageFilePath = nextNode.getTextContent(); // CreateNode with size @@ -380,17 +422,26 @@ protected static Target parseTargetElement (Element targetElement) throws IOExce final IStorageModule module = RandomAccessStorageModule.open(new File(storageFilePath), storageLength, create, kind); return new Target(targetName, targetAlias, module); - } protected static Node chopWhiteSpaces (final Node node) { Node toIterate = node; - while (toIterate instanceof Text && toIterate.getTextContent().trim().length() == 0) { + while (toIterate != null) { + if (toIterate instanceof Element) break; toIterate = toIterate.getNextSibling(); } return toIterate; } + protected static Node chopNotEquals (final Node node, final String tag) { + Node next = node; + while (next != null) { + if (Strings.nullToEmpty (next.getLocalName()).equals(tag)) break; + next = next.getNextSibling(); + } + return next; + } + public String getExternalTargetAddress() { return externalTargetAddress; } @@ -398,4 +449,17 @@ public String getExternalTargetAddress() { public int getExternalPort() { return externalPort; } + + public String getVendorID() { + return idVendor; + } + + public String getProductID() { + return idProduct; + } + + public String getProductRevisionLevel() { + return revProduct; + } + } diff --git a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java index bbd0c94c3..e736c0d3e 100644 --- a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java +++ b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java @@ -95,8 +95,10 @@ public TargetServer (final Configuration conf) { // read target settings from configuration file - LOGGER.debug(" target address: " + getConfig().getTargetAddress()); - LOGGER.debug(" port: " + getConfig().getPort()); + LOGGER.debug(" target address: " + getConfig().getTargetAddress()); + LOGGER.debug(" port: " + getConfig().getPort()); + LOGGER.debug(" external address: " + getConfig().getExternalTargetAddress()); + LOGGER.debug(" external port: " + getConfig().getExternalPort()); LOGGER.debug(" loading targets."); // open the storage medium List targetInfo = getConfig().getTargets(); @@ -104,7 +106,7 @@ public TargetServer (final Configuration conf) { targets.put(curTargetInfo.getTargetName(), curTargetInfo); // print configuration and medium details - LOGGER.debug(" target name: " + curTargetInfo.getTargetName() + " loaded."); + LOGGER.debug(" target name: " + curTargetInfo.getTargetName() + " loaded."); } this.deviceIdentificationVpdPage = new DeviceIdentificationVpdPage(this); diff --git a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java index c93e797bb..e9ccdaaec 100644 --- a/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java +++ b/bundles/target/src/main/java/org/jscsi/target/connection/stage/fullfeature/InquiryStage.java @@ -10,6 +10,7 @@ import org.jscsi.parser.BasicHeaderSegment; import org.jscsi.parser.ProtocolDataUnit; import org.jscsi.parser.scsi.SCSICommandParser; +import org.jscsi.target.Configuration; import org.jscsi.target.connection.phase.TargetFullFeaturePhase; import org.jscsi.target.scsi.IResponseData; import org.jscsi.target.scsi.cdb.InquiryCDB; @@ -82,7 +83,8 @@ public void execute (ProtocolDataUnit pdu) throws IOException , InterruptedExcep if (!cdb.getEnableVitalProductData()) { // ... the device server shall return the standard INQUIRY // data." - responseData = StandardInquiryData.getInstance(); + final Configuration config = connection.getTargetSession ().getTargetServer ().getConfig (); + responseData = new StandardInquiryData (config); } else { /* * SCSI initiator is requesting either "device identification" or "supported VPD pages" or this else diff --git a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/StandardInquiryData.java b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/StandardInquiryData.java index 2860fb5b9..34101b05c 100644 --- a/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/StandardInquiryData.java +++ b/bundles/target/src/main/java/org/jscsi/target/scsi/inquiry/StandardInquiryData.java @@ -3,19 +3,19 @@ import java.nio.ByteBuffer; +import org.jscsi.target.Configuration; import org.jscsi.target.scsi.IResponseData; import org.jscsi.target.scsi.cdb.ScsiOperationCode; +import org.jscsi.utils.Utils; + +import com.google.common.base.Strings; /** * The standard inquiry data, sent as a response to an {@link ScsiOperationCode#INQUIRY} command. - *

- * This class uses the singleton pattern since the returned standard inquiry data will always be the same. - *

- * Not all fields in the serialized form of the singleton have a corresponding member variable, only those fields - * containing ASCII information. - * + * * @author Andreas Ergenzinger + * @author CHEN Qingcan */ public final class StandardInquiryData implements IResponseData { @@ -36,25 +36,37 @@ public final class StandardInquiryData implements IResponseData { private static final int PRODUCT_REVISION_LEVEL_FIELD_POSITION = 32; private static final int PRODUCT_REVISION_LEVEL_FIELD_LENGTH = 4; - /** - * The singleton. - */ - private static StandardInquiryData instance; - - private StandardInquiryData () { - // singleton pattern - } - - /** - * Returns the one and only {@link StandardInquiryData} object. - * - * @return the one and only {@link StandardInquiryData} object - */ - public static StandardInquiryData getInstance () { - if (instance == null) instance = new StandardInquiryData(); - return instance; + private String inquiryVendorID, inquiryProductID, inquiryProductRevisionLevel; + + public StandardInquiryData (final Configuration config) { + inquiryVendorID = VENDOR_ID; + inquiryProductID = PRODUCT_ID; + inquiryProductRevisionLevel = PRODUCT_REVISION_LEVEL; + + if (config != null) { + String configVendorID = config.getVendorID (), + configProductID = config.getProductID (), + configProductRevisionLevel = config.getProductRevisionLevel (); + if (! Strings.isNullOrEmpty (configVendorID)) { + inquiryVendorID = configVendorID; + } + if (! Strings.isNullOrEmpty (configProductID)) { + inquiryProductID = configProductID; + } + if (! Strings.isNullOrEmpty (configProductRevisionLevel)) { + inquiryProductRevisionLevel = configProductRevisionLevel; + } + } + + inquiryVendorID = Utils.fixedLengthString + (inquiryVendorID, VENDOR_ID_FIELD_LENGTH, ' '); + inquiryProductID = Utils.fixedLengthString + (inquiryProductID, PRODUCT_ID_FIELD_LENGTH, ' '); + inquiryProductRevisionLevel = Utils.fixedLengthString + (inquiryProductRevisionLevel, PRODUCT_REVISION_LEVEL_FIELD_LENGTH, ' '); } + @Override public void serialize (ByteBuffer byteBuffer, int index) { // *** byte 0 **** @@ -143,27 +155,27 @@ public void serialize (ByteBuffer byteBuffer, int index) { * data identifying the vendor of the product. The T10 vendor identification shall be one assigned by INCITS, * but obviously that is not the case here. disyUKon */ - putString(byteBuffer, VENDOR_ID, index + VENDOR_ID_FIELD_POSITION, VENDOR_ID_FIELD_LENGTH); + putString(byteBuffer, inquiryVendorID, index + VENDOR_ID_FIELD_POSITION, VENDOR_ID_FIELD_LENGTH); // *** bytes 16 to 31 /* * PRODUCT IDENTIFICATION: The PRODUCT IDENTIFICATION field contains sixteen bytes of left-aligned ASCII data * defined by the vendor. */ - putString(byteBuffer, PRODUCT_ID, index + PRODUCT_ID_FIELD_POSITION, PRODUCT_ID_FIELD_LENGTH); + putString(byteBuffer, inquiryProductID, index + PRODUCT_ID_FIELD_POSITION, PRODUCT_ID_FIELD_LENGTH); // *** bytes 32 to 35 *** /* * PRODUCT REVISION LEVEL: The PRODUCT REVISION LEVEL field contains four bytes of left-aligned ASCII data * defined by the vendor. */ - putString(byteBuffer, PRODUCT_REVISION_LEVEL, index + PRODUCT_REVISION_LEVEL_FIELD_POSITION, PRODUCT_REVISION_LEVEL_FIELD_LENGTH); + putString(byteBuffer, inquiryProductRevisionLevel, index + PRODUCT_REVISION_LEVEL_FIELD_POSITION, PRODUCT_REVISION_LEVEL_FIELD_LENGTH); } /** * Puts up to fieldLength of the passed {@link String} into a {@link ByteBuffer} and fills the remaining * bytes with zeros. - * + * * @param byteBuffer where the {@link String}'s characters will be copied * @param string contains the characters to copy * @param position where the first character of the {@link String} will be put @@ -181,6 +193,7 @@ private void putString (final ByteBuffer byteBuffer, final String string, final byteBuffer.put((byte) 0); } + @Override public int size () { return SIZE; } diff --git a/bundles/target/src/main/resources/jscsi-target.xml b/bundles/target/src/main/resources/jscsi-target.xml index e8405ace1..b9e3a80a6 100644 --- a/bundles/target/src/main/resources/jscsi-target.xml +++ b/bundles/target/src/main/resources/jscsi-target.xml @@ -25,20 +25,37 @@ xsi:schemaLocation="http://www.jscsi.org/2010-04 jscsi-target.xsd"> - - box.vm:disk-1 - jSCSI Target - - /tmp/vbox.dat - - - + + box.vm:disk-1 + + jSCSI Target + + + /tmp/vbox.dat + + + + iqn.2010-04.local-test:disk-1 jSCSI Target /tmp/storage1.dat - + @@ -46,14 +63,46 @@ jSCSI Target /tmp/storage2.dat - + true +

0.0.0.0
+ + 127.0.0.1 + 3260 + + 3260 + + jSCSIorg + + iSCSI target + + 1.00 - diff --git a/bundles/target/src/main/resources/jscsi-target.xsd b/bundles/target/src/main/resources/jscsi-target.xsd index 7440f62e3..c936a9408 100644 --- a/bundles/target/src/main/resources/jscsi-target.xsd +++ b/bundles/target/src/main/resources/jscsi-target.xsd @@ -92,8 +92,18 @@ default="false" minOccurs="0" maxOccurs="1" /> + + + + + diff --git a/bundles/target/src/test/java/org/jscsi/target/connection/ConnectionTest.java b/bundles/target/src/test/java/org/jscsi/target/connection/ConnectionTest.java index d0295c0e6..da2a02683 100644 --- a/bundles/target/src/test/java/org/jscsi/target/connection/ConnectionTest.java +++ b/bundles/target/src/test/java/org/jscsi/target/connection/ConnectionTest.java @@ -25,6 +25,7 @@ import org.jscsi.parser.scsi.SCSIResponseParser; import org.jscsi.parser.scsi.SCSIResponseParser.ServiceResponse; import org.jscsi.parser.scsi.SCSIStatus; +import org.jscsi.target.Configuration; import org.jscsi.target.connection.phase.TargetFullFeaturePhase; import org.jscsi.target.connection.stage.TargetStage; import org.jscsi.target.connection.stage.fullfeature.FormatUnitStage; @@ -308,7 +309,8 @@ public ProtocolDataUnit check (final Connection pConnection) throws InterruptedE IResponseData responseData; if (!cdb.getEnableVitalProductData()) { - responseData = StandardInquiryData.getInstance(); + final Configuration config = pConnection.getTargetSession ().getTargetServer ().getConfig (); + responseData = new StandardInquiryData (config); } else { final VitalProductDataPageName pageName = cdb.getPageCode().getVitalProductDataPageName(); From 6cec3fcd159971d271038e103f954325dec7f679 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Mon, 29 Aug 2022 14:32:53 +0800 Subject: [PATCH 17/21] Skip user input target address step if configuration file parameter provided in TargetServer.main --- .../java/org/jscsi/target/TargetServer.java | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java index e736c0d3e..9ef18cc54 100644 --- a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java +++ b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java @@ -139,6 +139,32 @@ public static int getNextTargetTransferTag () { public static void main (String[] args) throws Exception { TargetServer target; + switch (args.length) { + case 0 : + target = new TargetServer(Configuration.create(chooseTargetAddress())); + break; + + case 1 : + // Checking if the schema file is at the default location + target = new TargetServer( + Configuration.create(Configuration.CONFIGURATION_SCHEMA_FILE.exists() ? + new FileInputStream(Configuration.CONFIGURATION_SCHEMA_FILE) : + TargetServer.class.getResourceAsStream("/jscsi-target.xsd"), + new FileInputStream(args[0]), "")); + break; + + case 2 : + target = new TargetServer(Configuration.create(new File(args[0]), new File(args[1]), "")); + break; + + default : + throw new IllegalArgumentException("Only zero or one Parameter (Path to Configuration-File) allowed!"); + } + + target.call(); + } + + private static String chooseTargetAddress () throws Exception { System.out.println("This system provides more than one IP Address to advertise.\n"); Enumeration interfaceEnum = NetworkInterface.getNetworkInterfaces(); @@ -176,30 +202,8 @@ public static void main (String[] args) throws Exception { } String targetAddress = addresses.get(chosenIndex).getHostAddress(); - System.out.println("Using ip address " + addresses.get(chosenIndex).getHostAddress()); - - - switch (args.length) { - case 0 : - target = new TargetServer(Configuration.create(targetAddress)); - break; - case 1 : - - // Checking if the schema file is at the default location - target = new TargetServer( - Configuration.create(Configuration.CONFIGURATION_SCHEMA_FILE.exists() ? - new FileInputStream(Configuration.CONFIGURATION_SCHEMA_FILE) : - TargetServer.class.getResourceAsStream("/jscsi-target.xsd"), - new FileInputStream(args[0]), targetAddress)); - break; - case 2 : - target = new TargetServer(Configuration.create(new File(args[0]), new File(args[1]), targetAddress)); - break; - default : - throw new IllegalArgumentException("Only zero or one Parameter (Path to Configuration-File) allowed!"); - } - - target.call(); + System.out.println("Using ip address " + targetAddress); + return targetAddress; } private class ConnectionHandler implements Callable { From a9deb1fab423f301506b304b39c40e43560a4432 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Mon, 29 Aug 2022 16:23:08 +0800 Subject: [PATCH 18/21] How to run the target as standalone executable jar --- README.md | 19 +++++++++++++++++++ .../jscsi/target/storage/IStorageModule.java | 6 +++--- .../src/main/resources/jscsi-target.xml | 8 ++++---- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 4ec022b14..00a105dd8 100644 --- a/README.md +++ b/README.md @@ -34,6 +34,25 @@ The schema and an example config are accessible as download as well and included For further documentation and as an example, please refer to the examples in the initiator- and target-module. +* Run the target as standalone executable jar + + 1. Put the target configuration XML file in a sub-folder named ```config```. + See also ```bundles/target/src/main/resources/jscsi-target.xml``` + 2. Put the log configuration XML file in the ```config``` sub-folder. + See also ```bundles/target/src/main/resources/logback.xml``` + 3. Put commons-{version}.jar, target-{version}.jar and their dependencies jar in a sub-folder named ```lib```. + 4. Run +```bash +java \ + --class-path "lib/*" \ + -Dlogback.configurationFile=config/logback.xml \ + org.jscsi.target.TargetServer \ + config/jscsi-target.xml \ + > log/console.log \ + 2>&1 \ +& +``` + ## Content * README: this readme file diff --git a/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java b/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java index 430772bb4..47cf49fd0 100644 --- a/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java +++ b/bundles/target/src/main/java/org/jscsi/target/storage/IStorageModule.java @@ -106,9 +106,9 @@ public interface IStorageModule extends Closeable { *

* Implements may keep the default empty method if no synchronization is needed. * - * @param syncLogicalBlockAddress LOGICAL BLOCK ADDRESS field - * @param syncNumberOfBlocks NUMBER OF BLOCKS field - * @throws IOException + * @param syncLogicalBlockAddress LOGICAL BLOCK ADDRESS field + * @param syncNumberOfBlocks NUMBER OF BLOCKS field + * @throws IOException */ default void syncCahe (long syncLogicalBlockAddress, int syncNumberOfBlocks) throws IOException {} diff --git a/bundles/target/src/main/resources/jscsi-target.xml b/bundles/target/src/main/resources/jscsi-target.xml index b9e3a80a6..cc5bac42c 100644 --- a/bundles/target/src/main/resources/jscsi-target.xml +++ b/bundles/target/src/main/resources/jscsi-target.xml @@ -32,7 +32,7 @@ jSCSIorg iSCSI target 1.00 From 47d076e6ee3b06c77f981803bff599c9bcd57206 Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Tue, 30 Aug 2022 15:44:11 +0800 Subject: [PATCH 19/21] Add extended storage class configuration in XML file --- .../main/java/org/jscsi/target/Configuration.java | 13 +++++++++++++ .../main/java/org/jscsi/target/TargetServer.java | 2 +- bundles/target/src/main/resources/jscsi-target.xml | 4 ++++ 3 files changed, 18 insertions(+), 1 deletion(-) diff --git a/bundles/target/src/main/java/org/jscsi/target/Configuration.java b/bundles/target/src/main/java/org/jscsi/target/Configuration.java index f6edee4e8..c3adc9654 100644 --- a/bundles/target/src/main/java/org/jscsi/target/Configuration.java +++ b/bundles/target/src/main/java/org/jscsi/target/Configuration.java @@ -61,9 +61,11 @@ public class Configuration { public static final String ELEMENT_ASYNCFILESTORAGE = "AsyncFileStorage"; public static final String ELEMENT_JCLOUDSSTORAGE = "JCloudsStorage"; public static final String ELEMENT_FILESTORAGE = "FileStorage"; + public static final String ELEMENT_EXTENDEDSTORAGE = "ExtendedStorage"; public static final String ELEMENT_PATH= "Path"; public static final String ELEMENT_CREATE = "Create"; public static final String ATTRIBUTE_SIZE = "size"; + public static final String ATTRIBUTE_CLASS = "class"; // Global configuration elements public static final String ELEMENT_ALLOWSLOPPYNEGOTIATION = "AllowSloppyNegotiation"; @@ -396,6 +398,17 @@ protected static Target parseTargetElement (Element targetElement) throws IOExce case ELEMENT_JCLOUDSSTORAGE : kind = JCloudsStorageModule.class; break; + case ELEMENT_EXTENDEDSTORAGE : + Node attrClass = nextNode.getAttributes().getNamedItem(ATTRIBUTE_CLASS); + String nameClass = attrClass.getTextContent(); + try { + @SuppressWarnings ("unchecked") + Class unc = (Class) Class.forName (nameClass); + kind = unc; + } catch (ClassNotFoundException e) { + throw new IOException ("Storage class not found: " + nameClass + " (" + e.getMessage() + ")"); + } + break; default: throw new IOException ("Unknown storage: " + nextNode.getLocalName()); } diff --git a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java index 9ef18cc54..d42c1213a 100644 --- a/bundles/target/src/main/java/org/jscsi/target/TargetServer.java +++ b/bundles/target/src/main/java/org/jscsi/target/TargetServer.java @@ -90,7 +90,6 @@ public class TargetServer implements Callable { public TargetServer (final Configuration conf) { this.config = conf; - LOGGER.debug("Starting jSCSI-target: "); // read target settings from configuration file @@ -107,6 +106,7 @@ public TargetServer (final Configuration conf) { targets.put(curTargetInfo.getTargetName(), curTargetInfo); // print configuration and medium details LOGGER.debug(" target name: " + curTargetInfo.getTargetName() + " loaded."); + LOGGER.debug(" storage module: " + curTargetInfo.getStorageModule().getClass().getName()); } this.deviceIdentificationVpdPage = new DeviceIdentificationVpdPage(this); diff --git a/bundles/target/src/main/resources/jscsi-target.xml b/bundles/target/src/main/resources/jscsi-target.xml index cc5bac42c..8b708627b 100644 --- a/bundles/target/src/main/resources/jscsi-target.xml +++ b/bundles/target/src/main/resources/jscsi-target.xml @@ -38,6 +38,10 @@ * SyncFileStorage - Implemented by SynchronizedRandomAccessStorageModule class. Adds synchronized keyword around read, write methods. * JCloudsStorage - Implemented by JCloudsStorageModule class. + * ExtendedStorage - Implemented by a class specified in the class attribute. e.g. + + The class implements IStorageModule interface, and a constructor with two parameters + (long lStorageLengthIn512BytesBlocks, File fStorageMedium). --> /tmp/vbox.dat From f7e39a07cf05da4c578f3842423cbc6833dde75d Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Tue, 30 Aug 2022 16:04:59 +0800 Subject: [PATCH 20/21] Enrich listening address configuration comments --- .../target/src/main/java/org/jscsi/target/Configuration.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bundles/target/src/main/java/org/jscsi/target/Configuration.java b/bundles/target/src/main/java/org/jscsi/target/Configuration.java index c3adc9654..f76585519 100644 --- a/bundles/target/src/main/java/org/jscsi/target/Configuration.java +++ b/bundles/target/src/main/java/org/jscsi/target/Configuration.java @@ -246,6 +246,8 @@ public static Configuration create (final String pTargetAddress) throws SAXExcep /** * Reads the given configuration file in memory and creates a DOM representation. * + * @param pTargetAddress Get from configuration XML if this parameter is null or empty. + * * @throws SAXException If this operation is supported but failed for some reason. * @throws ParserConfigurationException If a {@link DocumentBuilder} cannot be created which satisfies the * configuration requested. @@ -270,6 +272,8 @@ public static Configuration create (final File schemaLocation, final File config /** * Reads the given configuration file in memory and creates a DOM representation. * + * @param pTargetAddress Get from configuration XML if this parameter is null or empty. + * * @throws SAXException If this operation is supported but failed for some reason. * @throws ParserConfigurationException If a {@link DocumentBuilder} cannot be created which satisfies the * configuration requested. From 48e5e3e04905f07282b8201439800617f9dbad3d Mon Sep 17 00:00:00 2001 From: ActiveVolcano Date: Tue, 30 Aug 2022 16:05:23 +0800 Subject: [PATCH 21/21] Fix Maven warnings --- pom.xml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/pom.xml b/pom.xml index 4d7c969d6..66d35e265 100644 --- a/pom.xml +++ b/pom.xml @@ -58,6 +58,7 @@ maven-compiler-plugin + 3.1 ${maven.compiler.source} ${maven.compiler.target} @@ -66,6 +67,7 @@ maven-resources-plugin + 2.6 ${project.build.sourceEncoding} @@ -85,6 +87,7 @@ maven-source-plugin + 3.2.1 attach-sources @@ -97,6 +100,7 @@ maven-jar-plugin + 2.4 @@ -108,6 +112,7 @@ maven-gpg-plugin + 3.0.1 verify @@ -128,6 +133,7 @@ org.jacoco jacoco-maven-plugin + 0.8.8 agent-for-ut @@ -154,6 +160,7 @@ org.sonatype.plugins nexus-staging-maven-plugin + 1.6.13 true ossrh