From 9e6671c0aa099bfe2e5b9c3c4eeb5ed895fb993d Mon Sep 17 00:00:00 2001 From: Jesse Gorzinski <17914061+ThePrez@users.noreply.github.com> Date: Thu, 27 Apr 2023 19:10:21 -0500 Subject: [PATCH 1/2] (WIP) IFS extended attributes Co-Authored-By: Zhanghze <106234365+Zhanghze@users.noreply.github.com> Signed-off-by: Jesse Gorzinski <17914061+ThePrez@users.noreply.github.com> --- .../java/com/ibm/as400/access/IFSFile.java | 32 ++- .../access/IFSFileDescriptorImplRemote.java | 189 +++++++++++++++- .../com/ibm/as400/access/IFSFileImpl.java | 4 + .../ibm/as400/access/IFSFileImplProxy.java | 19 ++ .../ibm/as400/access/IFSFileImplRemote.java | 213 +++++++++++++++++- .../com/ibm/as400/access/IFSGetEAsRep.java | 92 ++++++++ .../com/ibm/as400/access/IFSGetEAsReq.java | 43 ++++ .../com/ibm/as400/access/IFSOpenNodeReq.java | 18 +- 8 files changed, 587 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/ibm/as400/access/IFSGetEAsRep.java create mode 100644 src/main/java/com/ibm/as400/access/IFSGetEAsReq.java diff --git a/src/main/java/com/ibm/as400/access/IFSFile.java b/src/main/java/com/ibm/as400/access/IFSFile.java index 437d05ef9..7de4fb985 100644 --- a/src/main/java/com/ibm/as400/access/IFSFile.java +++ b/src/main/java/com/ibm/as400/access/IFSFile.java @@ -1497,8 +1497,8 @@ public String getOwnerName() return impl_.getOwnerNameByUserHandle(false); //@AC7 } else - //return impl_.getOwnerName(); //@AE8D - //@AE8A + //return impl_.getOwnerName(); + //TODO { String pathUpperCase = path_.toUpperCase(system_.getLocale()); if (pathUpperCase.startsWith("/QSYS.LIB") && !pathUpperCase.endsWith(".FILE")) { @@ -1794,7 +1794,7 @@ For further information, refer to the specification of the QDBRTVFD (Retrieve Da public boolean isSourcePhysicalFile() throws AS400Exception, AS400SecurityException, IOException { - String pathUpperCase = path_.toUpperCase(system_.getLocale()); //@AE8A + String pathUpperCase = path_.toUpperCase(system_.getLocale()); //TODO fix path lower case issue if (!pathUpperCase.endsWith(".FILE") || pathUpperCase.indexOf("/QSYS.LIB") == -1 || !getSubtype().equals("PF")) @@ -3741,6 +3741,30 @@ public String toString() { return path_; } - + + //TODO get Text 'description' of file/directory + public String getText() throws IOException, AS400SecurityException { + if (impl_ == null) + chooseImpl(); + return impl_.getText(); + } + + public String getCodePage() throws IOException, AS400SecurityException { + if (impl_ == null) + chooseImpl(); + return impl_.getCodePage(); + } + + public Hashtable getEAs() throws IOException, AS400SecurityException { + if (impl_ == null) + chooseImpl(); + return impl_.getExtendedAttributes(); + } + /* + public void setText(String fileText) { + if (impl_ == null) + impl_.setText(fileText); + } + */ } diff --git a/src/main/java/com/ibm/as400/access/IFSFileDescriptorImplRemote.java b/src/main/java/com/ibm/as400/access/IFSFileDescriptorImplRemote.java index 9a62b7c3e..5062b502a 100644 --- a/src/main/java/com/ibm/as400/access/IFSFileDescriptorImplRemote.java +++ b/src/main/java/com/ibm/as400/access/IFSFileDescriptorImplRemote.java @@ -29,6 +29,7 @@ import java.io.IOException; import java.io.InterruptedIOException; +import java.util.Hashtable; import java.util.Vector; @@ -80,6 +81,7 @@ class IFSFileDescriptorImplRemote String fileOwnerName_ = null; boolean isDirectory_ = false; //@AC7 End + Hashtable fileText_ = null; //TODO // Static initialization code. static @@ -2111,7 +2113,7 @@ else if (ds instanceof IFSReturnCodeRep) //@AC7A Start private void retrieveAttributes(ClientAccessDataStream ds, int objectHandle) throws IOException, AS400SecurityException { fileAsp_ = ((IFSLookupRep) ds).getASP(); - //if (isDirectory_) { @AE8D + //if (isDirectory_) { TODO remove fileOwnerName_ = ((IFSLookupRep) ds).getOwnerName(system_.getCcsid()); fileDataCCSID_ = ((IFSLookupRep) ds).getCCSID(serverDatastreamLevel_); //} @@ -2165,6 +2167,191 @@ else if (ds instanceof IFSReturnCodeRep) } } //@AC7A End + + //TODO + /* + public IFSGetEAsRep listEAs() throws IOException, AS400SecurityException { + IFSGetEAsRep reply = null; + int fileHandle = UNINITIALIZED; + boolean usedGlobalHandle = false; + try { + // Open the file, and obtain a file handle. + if (fileHandle_ != UNINITIALIZED) + { + fileHandle = fileHandle_; + usedGlobalHandle = true; + } + else + { + fileHandle = createFileHandle(); //@KKBC + if (fileHandle == UNINITIALIZED) + { + if (Trace.traceOn_) Trace.log(Trace.ERROR, "Unable to create handle to file " + path_ + ". IFSReturnCodeRep return code", errorRC_); + return null; + } + } + IFSGetEAsReq req = new IFSGetEAsReq(fileHandle, serverDatastreamLevel_); + + int rc = 0; + ClientAccessDataStream ds = null; + try + { + ds = (ClientAccessDataStream) server_.sendAndReceive(req); + } + catch(ConnectionDroppedException e) + { + Trace.log(Trace.ERROR, "Byte stream server connection lost."); + connectionDropped(e); + } + catch(InterruptedException e) + { + Trace.log(Trace.ERROR, "Interrupted"); + InterruptedIOException throwException = new InterruptedIOException(e.getMessage()); + try { + throwException.initCause(e); + } catch (Throwable t) {} + throw throwException; + } + + if (ds instanceof IFSGetEAsRep) + { + reply = (IFSGetEAsRep) ds; + return reply; + } + else if (ds instanceof IFSReturnCodeRep) + { + rc = ((IFSReturnCodeRep) ds).getReturnCode(); + if (rc != IFSReturnCodeRep.SUCCESS) + { + Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc); + throw new ExtendedIOException(path_, rc); + } + } + else + { + // Unknown data stream. + Trace.log(Trace.ERROR, "Unknown reply data stream", ds.data_); + throw new + InternalErrorException(Integer.toHexString(ds.getReqRepID()), + InternalErrorException.DATA_STREAM_UNKNOWN); + } + } + finally { + if(!usedGlobalHandle && fileHandle != UNINITIALIZED) + close(fileHandle); + } + return reply; + }*/ + + public IFSGetEAsRep getExtendedAttributes(byte[][] eaNamelist, int eaNameLength, int ccsid) throws IOException, AS400SecurityException { + IFSGetEAsRep reply = null; + ClientAccessDataStream ds = null; + connect(); + //create user handle + int userHandle = UNINITIALIZED, objectHandle = UNINITIALIZED, nodeObjectHandle = UNINITIALIZED; + try{ + userHandle = system_.createUserHandle(); + try + { + byte[] path = getConverter().stringToByteArray(path_); + //IFSLookupReq req = new IFSLookupReq(path, preferredServerCCSID_, userHandle, IFSLookupReq.OA2, 0, 0); + //IFSLookupReq req = new IFSLookupReq(path, preferredServerCCSID_, userHandle, IFSLookupReq.OA12, IFSObjAttrs1.OWNERANAME_ASP_FLAS, 0); //@AC7A + IFSLookupReq req = new IFSLookupReq(path, preferredServerCCSID_,userHandle); + ds = (ClientAccessDataStream) server_.sendAndReceive(req); + int rc = 0; + if (ds instanceof IFSLookupRep) + { + objectHandle = ((IFSLookupRep) ds).getHandle(); + //Open Node to get object request; + IFSOpenNodeReq nodeReq = new IFSOpenNodeReq(objectHandle, serverDatastreamLevel_); + ds = null; + ds = (ClientAccessDataStream) server_.sendAndReceive(nodeReq); + if (ds instanceof IFSOpenNodeRep) { + nodeObjectHandle = ((IFSOpenNodeRep) ds).getObjectHandle(); + IFSGetEAsReq eaReq = new IFSGetEAsReq(nodeObjectHandle, eaNamelist, eaNameLength, ccsid, serverDatastreamLevel_); + ds = (ClientAccessDataStream) server_.sendAndReceive(eaReq); + if (ds instanceof IFSGetEAsRep) { + reply = (IFSGetEAsRep) ds; + } else if (ds instanceof IFSReturnCodeRep) + { + rc = ((IFSReturnCodeRep) ds).getReturnCode(); + if (rc != IFSReturnCodeRep.SUCCESS) + { + Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc); + } + throw new ExtendedIOException(path_, rc); + } + else + { + // Unknown data stream. + Trace.log(Trace.ERROR, "Unknown reply data stream", + ds.getReqRepID()); + throw new + InternalErrorException(Integer.toHexString(ds.getReqRepID()), + InternalErrorException.DATA_STREAM_UNKNOWN); + } + } else if (ds instanceof IFSReturnCodeRep) + { + rc = ((IFSReturnCodeRep) ds).getReturnCode(); + if (rc != IFSReturnCodeRep.SUCCESS) + { + Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc); + } + throw new ExtendedIOException(path_, rc); + } + else + { + // Unknown data stream. + Trace.log(Trace.ERROR, "Unknown reply data stream", + ds.getReqRepID()); + throw new + InternalErrorException(Integer.toHexString(ds.getReqRepID()), + InternalErrorException.DATA_STREAM_UNKNOWN); + } + } + else if (ds instanceof IFSReturnCodeRep) + { + rc = ((IFSReturnCodeRep) ds).getReturnCode(); + if (rc != IFSReturnCodeRep.SUCCESS) + { + Trace.log(Trace.ERROR, "IFSReturnCodeRep return code", rc); + } + throw new ExtendedIOException(path_, rc); + } + else + { + // Unknown data stream. + Trace.log(Trace.ERROR, "Unknown reply data stream", + ds.getReqRepID()); + throw new + InternalErrorException(Integer.toHexString(ds.getReqRepID()), + InternalErrorException.DATA_STREAM_UNKNOWN); + } + + } catch(ConnectionDroppedException e) + { + Trace.log(Trace.ERROR, "Byte stream server connection lost."); + connectionDropped(e); + } + catch(InterruptedException e) + { + Trace.log(Trace.ERROR, "Interrupted"); + InterruptedIOException throwException = new InterruptedIOException(e.getMessage()); + try { + throwException.initCause(e); + } catch (Throwable t) {} + throw throwException; + } + }finally{ + if(nodeObjectHandle != UNINITIALIZED) + freeHandle(nodeObjectHandle); + if(objectHandle != UNINITIALIZED) + freeHandle(objectHandle); + } + return reply; + } + + } diff --git a/src/main/java/com/ibm/as400/access/IFSFileImpl.java b/src/main/java/com/ibm/as400/access/IFSFileImpl.java index bcbc78164..bf90444a8 100644 --- a/src/main/java/com/ibm/as400/access/IFSFileImpl.java +++ b/src/main/java/com/ibm/as400/access/IFSFileImpl.java @@ -20,6 +20,7 @@ package com.ibm.as400.access; import java.io.IOException; +import java.util.Hashtable; /** Specifies the methods which the implementation objects for the IFSFile class @@ -97,6 +98,9 @@ int renameTo(IFSFileImpl file) int getCCSID(boolean retrieveAll) throws IOException, AS400SecurityException; String getOwnerName(boolean retrieveAll) throws IOException, AS400SecurityException; //@AC7 End + String getText() throws IOException, AS400SecurityException; + String getCodePage() throws IOException, AS400SecurityException; + Hashtable getExtendedAttributes() throws IOException, AS400SecurityException; } diff --git a/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java b/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java index 56de5c386..86996aec9 100644 --- a/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java +++ b/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.lang.reflect.InvocationTargetException; +import java.util.Hashtable; /** @@ -715,4 +716,22 @@ public void setSystem(AS400Impl system) } } + @Override + public String getText() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + @Override + public String getCodePage() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + + @Override + public Hashtable getExtendedAttributes() { + // TODO Auto-generated method stub + throw new UnsupportedOperationException(); + } + } diff --git a/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java b/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java index 24dbd1724..4de4d2dd9 100644 --- a/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java +++ b/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java @@ -33,14 +33,14 @@ import java.util.Arrays; import java.util.Date; import java.util.Hashtable; +import java.util.Iterator; import java.util.Vector; import java.beans.PropertyVetoException; /** Provides a full remote implementation for the IFSFile class. **/ -class IFSFileImplRemote -implements IFSFileImpl +class IFSFileImplRemote implements IFSFileImpl { // Used for debugging only. This should always be false for production. // When this is false, all debug code will theoretically compile out. @@ -74,6 +74,8 @@ class IFSFileImplRemote AS400Server.addReplyStream(new IFSReturnCodeRep(), AS400.FILE); AS400Server.addReplyStream(new IFSLookupRep(), AS400.FILE); AS400Server.addReplyStream(new IFSGetFileSystemRep(), AS400.FILE); + AS400Server.addReplyStream(new IFSOpenNodeRep(), AS400.FILE); + AS400Server.addReplyStream(new IFSGetEAsRep(), AS400.FILE); } transient private IFSListAttrsRep attributesReply_; // "list attributes" reply @@ -2990,5 +2992,212 @@ protected synchronized void setupRemoteCommand() throws IOException rmtCmd_.setSystem(fd_.system_); } } + + //TODO @ZZA + public Hashtable getExtendedAttributes() throws IOException, AS400SecurityException { + Hashtable EAs = new Hashtable(); + fd_.connect(); + try { + IFSGetEAsRep reply = null; + //reply = fd_.listEAs(); + byte[] eaName_SUBJECT = fd_.converter_.stringToByteArray(".SUBJECT"); + byte[] eaName_CODEPAGE = fd_.converter_.stringToByteArray(".CODEPAGE"); + byte[] eaName_TYPE = fd_.converter_.stringToByteArray(".TYPE"); + int eaNameBytesLength = eaName_SUBJECT.length + eaName_CODEPAGE.length + eaName_TYPE.length; + byte[][] eaNamesList = new byte[][] { eaName_SUBJECT, eaName_CODEPAGE, eaName_TYPE }; + + reply = fd_.getExtendedAttributes(eaNamesList,eaNameBytesLength,fd_.preferredServerCCSID_); + if (reply != null) { + EAs = reply.getExtendedAttributeValues(); + } else { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "getOwnerName: " + "IFSReturnCodeRep return code", fd_.errorRC_); + if (fd_.errorRC_ == IFSReturnCodeRep.FILE_NOT_FOUND || fd_.errorRC_ == IFSReturnCodeRep.PATH_NOT_FOUND) + { + throw new ExtendedIOException(fd_.path_, ExtendedIOException.PATH_NOT_FOUND); + } + } + } catch (ExtendedIOException e) { + if (e.getReturnCode() == ExtendedIOException.DIR_ENTRY_EXISTS) { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "Unable to determine owner of directory.", e); + } else + throw e; + } + return EAs; + } + + + public String getText() throws IOException, AS400SecurityException { + String fileText = ""; + // Ensure that we are connected to the server. + fd_.connect(); + // Convert the path name to the server CCSID. + byte[] pathname = fd_.converter_.stringToByteArray(fd_.path_); + boolean needCodePage; + if (fd_.getSystemVRM() >= 0x00060100 && + fd_.path_.indexOf("/QSYS.LIB") != -1) { + needCodePage = true; + } + else + needCodePage = false; + // Prepare the List Attributes request. + // Set up the list of Extended Attributes Names. + //.SUBJECT + //.TYPE + //.CODEPAGE + byte[] eaName_SUBJECT = fd_.converter_.stringToByteArray(".SUBJECT"); + int eaNameBytesLength; + byte[][] eaNamesList; + // Special handling for QSYS files, starting in V6R1. + // Starting in V6R1, for QSYS files, the ".TYPE" EA value field is returned in the CCSID of the object. + // Prior to V6R1, the field is always returned in EBCDIC. + if (needCodePage) + { + byte[] eaName_CODEPAGE = fd_.converter_.stringToByteArray(".CODEPAGE"); + eaNameBytesLength = eaName_SUBJECT.length + eaName_CODEPAGE.length; + eaNamesList = new byte[][] { eaName_SUBJECT, eaName_CODEPAGE }; + } + else // not in QSYS, or pre-V6R1 + { + eaNameBytesLength = eaName_SUBJECT.length; + eaNamesList = new byte[][] { eaName_SUBJECT }; + } + + IFSListAttrsReq req = new IFSListAttrsReq(pathname, fd_.preferredServerCCSID_, + IFSListAttrsReq.NO_AUTHORITY_REQUIRED, NO_MAX_GET_COUNT, + null, false, eaNamesList, eaNameBytesLength, false, fd_.patternMatching_); // @C3c + + Vector replys = fd_.listAttributes(req); + + // Verify that we got at least one reply. + if (replys == null) { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "Received null from listAttributes(req)."); + } + else if (replys.size() == 0) { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "Received no replies from listAttributes(req)."); + } + else + { + if (replys.size() > 1) { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "Received multiple replies from listAttributes(req) (" + + replys.size() + ")"); + } + IFSListAttrsRep reply = (IFSListAttrsRep)replys.elementAt(0); + Hashtable extendedAttributes = reply.getExtendedAttributeValues(); + byte[] fileTextBytes = (byte[])extendedAttributes.get(".SUBJECT"); + if (fileTextBytes != null) + { + int ccsid; + if (!needCodePage) { + ccsid = 37; // the returned bytes are in EBCDIC + } + else { + // Get the ".CODEPAGE" extended attribute value from the reply. + byte[] codepageAsBytes = (byte[])extendedAttributes.get(".CODEPAGE"); + // The .CODEPAGE attribute is returned as 2 bytes in little-endian format. + // Therefore we need to swap the bytes. + byte[] swappedBytes = new byte[2]; // the codepage is returned in 2 bytes + swappedBytes[0] = codepageAsBytes[1]; + swappedBytes[1] = codepageAsBytes[0]; + ccsid = BinaryConverter.byteArrayToUnsignedShort(swappedBytes,0); + if (ccsid == 1400) ccsid = 1200; // codepage 1400 corresponds to CCSID 1200 + } + try { + fileText = (new CharConverter(ccsid)).byteArrayToString(fileTextBytes, 0).trim(); + } + catch (java.io.UnsupportedEncodingException e) { + Trace.log(Trace.ERROR, "Unrecognized codepage returned: " + ccsid, e); + fileText = "??"; + } + } + } + return fileText; + } + + public String getCodePage() throws IOException, AS400SecurityException { + String fileText = ""; + // Ensure that we are connected to the server. + fd_.connect(); + // Convert the path name to the server CCSID. + byte[] pathname = fd_.converter_.stringToByteArray(fd_.path_); + boolean needCodePage; + if (fd_.getSystemVRM() >= 0x00060100 && + fd_.path_.indexOf("/QSYS.LIB") != -1) { + needCodePage = true; + } + else needCodePage = false; + // Prepare the List Attributes request. + // Set up the list of Extended Attributes Names. + //.SUBJECT + //.TYPE + //.CODEPAGE + byte[] eaName_CODEPAGE = fd_.converter_.stringToByteArray(".CODEPAGE"); + int eaNameBytesLength; + byte[][] eaNamesList; + // Special handling for QSYS files, starting in V6R1. + // Starting in V6R1, for QSYS files, the ".TYPE" EA value field is returned in the CCSID of the object. + // Prior to V6R1, the field is always returned in EBCDIC. + /* + if (needCodePage) + { + byte[] eaName_CODEPAGE = fd_.converter_.stringToByteArray(".CODEPAGE"); + eaNameBytesLength = eaName_SUBJECT.length + eaName_CODEPAGE.length; + eaNamesList = new byte[][] { eaName_SUBJECT, eaName_CODEPAGE }; + } + else // not in QSYS, or pre-V6R1 + {*/ + eaNameBytesLength = eaName_CODEPAGE.length; + eaNamesList = new byte[][] { eaName_CODEPAGE }; + + + IFSListAttrsReq req = new IFSListAttrsReq(pathname, fd_.preferredServerCCSID_, + IFSListAttrsReq.NO_AUTHORITY_REQUIRED, NO_MAX_GET_COUNT, + null, false, eaNamesList, eaNameBytesLength, false, fd_.patternMatching_); // @C3c + + Vector replys = fd_.listAttributes(req); + + // Verify that we got at least one reply. + if (replys == null) { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "Received null from listAttributes(req)."); + } + else if (replys.size() == 0) { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "Received no replies from listAttributes(req)."); + } + else + { + if (replys.size() > 1) { + if (Trace.traceOn_) Trace.log(Trace.WARNING, "Received multiple replies from listAttributes(req) (" + + replys.size() + ")"); + } + IFSListAttrsRep reply = (IFSListAttrsRep)replys.elementAt(0); + Hashtable extendedAttributes = reply.getExtendedAttributeValues(); + byte[] fileTextBytes = (byte[])extendedAttributes.get(".CODEPAGE"); + if (fileTextBytes != null) + { + int ccsid; + //if (!needCodePage) { + ccsid = 37; // the returned bytes are in EBCDIC + /*} + else { + // Get the ".CODEPAGE" extended attribute value from the reply. + byte[] codepageAsBytes = (byte[])extendedAttributes.get(".CODEPAGE"); + // The .CODEPAGE attribute is returned as 2 bytes in little-endian format. + // Therefore we need to swap the bytes. + byte[] swappedBytes = new byte[2]; // the codepage is returned in 2 bytes + swappedBytes[0] = codepageAsBytes[1]; + swappedBytes[1] = codepageAsBytes[0]; + ccsid = BinaryConverter.byteArrayToUnsignedShort(swappedBytes,0); + if (ccsid == 1400) ccsid = 1200; // codepage 1400 corresponds to CCSID 1200 + }*/ + try { + fileText = (new CharConverter(ccsid)).byteArrayToString(fileTextBytes, 0).trim(); + } + catch (java.io.UnsupportedEncodingException e) { + Trace.log(Trace.ERROR, "Unrecognized codepage returned: " + ccsid, e); + fileText = "??"; + } + } + } + return fileText; + } } diff --git a/src/main/java/com/ibm/as400/access/IFSGetEAsRep.java b/src/main/java/com/ibm/as400/access/IFSGetEAsRep.java new file mode 100644 index 000000000..edf3902e1 --- /dev/null +++ b/src/main/java/com/ibm/as400/access/IFSGetEAsRep.java @@ -0,0 +1,92 @@ +package com.ibm.as400.access; + +import java.util.Hashtable; + +public class IFSGetEAsRep extends IFSDataStream { + private static final String copyright = "Copyright (C) 2004-2004 International Business Machines Corporation and others."; + private static final int TEMPLATE_LENGTH = 4; + private static final boolean DEBUG = true; + + public Object getNewDataStream() + { + return new IFSGetEAsRep(); + } + + + public Hashtable getExtendedAttributeValues() + { + Hashtable results = new Hashtable(); + + // The offset to the start of the "optional/variable section" depends on the datastream level. + int optionalSectionOffset = HEADER_LENGTH + TEMPLATE_LENGTH; + + // Step through the optional fields, looking for the "EA list" field (code point 0x0009). + Trace.log(Trace.INFORMATION, "Extended Attribute returned: data length " + data_.length); + int curLL_offset = optionalSectionOffset; + int curLL = get32bit(curLL_offset); // list length + int curCP = get16bit(curLL_offset+4); // code point + int eaListOffset; // offset to start of Extended Attr list + + while (curCP != 0x0009 && (curLL_offset+curLL+6 <= data_.length)) + { + curLL_offset += curLL; + curLL = get32bit(curLL_offset); + curCP = get16bit(curLL_offset+4); + } + + if (curCP == 0x0009) + { + // We found the start of the Extended Attributes list. + eaListOffset = curLL_offset; // offset to "EA List Length" field + } + else + { + if (Trace.isTraceOn()) Trace.log(Trace.DIAGNOSTIC, "No Extended Attributes were returned."); + return results; // empty hashtable + } + + byte[] eaVal = null; + int eaCount = get16bit(eaListOffset+6); // number of EA structures returned + if (DEBUG) System.out.println("DEBUG Number of EA structures returned: " + eaCount); + + // Advance the offset, to point to the start of first repeating EA struct. + int offset = eaListOffset+8; + + for (int i=0; i Date: Thu, 27 Apr 2023 20:01:41 -0500 Subject: [PATCH 2/2] Add hashtable types Signed-off-by: Jesse Gorzinski <17914061+ThePrez@users.noreply.github.com> --- src/main/java/com/ibm/as400/access/IFSFileImpl.java | 2 +- src/main/java/com/ibm/as400/access/IFSFileImplProxy.java | 2 +- src/main/java/com/ibm/as400/access/IFSFileImplRemote.java | 6 +++--- src/main/java/com/ibm/as400/access/IFSGetEAsRep.java | 4 ++-- src/main/java/com/ibm/as400/access/IFSListAttrsRep.java | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/ibm/as400/access/IFSFileImpl.java b/src/main/java/com/ibm/as400/access/IFSFileImpl.java index bf90444a8..12c3c7e84 100644 --- a/src/main/java/com/ibm/as400/access/IFSFileImpl.java +++ b/src/main/java/com/ibm/as400/access/IFSFileImpl.java @@ -100,7 +100,7 @@ int renameTo(IFSFileImpl file) //@AC7 End String getText() throws IOException, AS400SecurityException; String getCodePage() throws IOException, AS400SecurityException; - Hashtable getExtendedAttributes() throws IOException, AS400SecurityException; + Hashtable getExtendedAttributes() throws IOException, AS400SecurityException; } diff --git a/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java b/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java index 86996aec9..31b5c1736 100644 --- a/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java +++ b/src/main/java/com/ibm/as400/access/IFSFileImplProxy.java @@ -729,7 +729,7 @@ public String getCodePage() { } @Override - public Hashtable getExtendedAttributes() { + public Hashtable getExtendedAttributes() { // TODO Auto-generated method stub throw new UnsupportedOperationException(); } diff --git a/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java b/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java index 4de4d2dd9..8ae14e155 100644 --- a/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java +++ b/src/main/java/com/ibm/as400/access/IFSFileImplRemote.java @@ -1532,7 +1532,7 @@ else if (replys.size() == 0) { replys.size() + ")"); } IFSListAttrsRep reply = (IFSListAttrsRep)replys.elementAt(0); - Hashtable extendedAttributes = reply.getExtendedAttributeValues(); + Hashtable extendedAttributes = reply.getExtendedAttributeValues(); byte[] subtypeAsBytes = (byte[])extendedAttributes.get(".TYPE"); if (subtypeAsBytes != null) { @@ -2994,8 +2994,8 @@ protected synchronized void setupRemoteCommand() throws IOException } //TODO @ZZA - public Hashtable getExtendedAttributes() throws IOException, AS400SecurityException { - Hashtable EAs = new Hashtable(); + public Hashtable getExtendedAttributes() throws IOException, AS400SecurityException { + Hashtable EAs = new Hashtable(); fd_.connect(); try { IFSGetEAsRep reply = null; diff --git a/src/main/java/com/ibm/as400/access/IFSGetEAsRep.java b/src/main/java/com/ibm/as400/access/IFSGetEAsRep.java index edf3902e1..008bb8402 100644 --- a/src/main/java/com/ibm/as400/access/IFSGetEAsRep.java +++ b/src/main/java/com/ibm/as400/access/IFSGetEAsRep.java @@ -13,9 +13,9 @@ public Object getNewDataStream() } - public Hashtable getExtendedAttributeValues() + public Hashtable getExtendedAttributeValues() { - Hashtable results = new Hashtable(); + Hashtable results = new Hashtable(); // The offset to the start of the "optional/variable section" depends on the datastream level. int optionalSectionOffset = HEADER_LENGTH + TEMPLATE_LENGTH; diff --git a/src/main/java/com/ibm/as400/access/IFSListAttrsRep.java b/src/main/java/com/ibm/as400/access/IFSListAttrsRep.java index 87c25b17c..60d5e2168 100644 --- a/src/main/java/com/ibm/as400/access/IFSListAttrsRep.java +++ b/src/main/java/com/ibm/as400/access/IFSListAttrsRep.java @@ -126,9 +126,9 @@ long getCreationDate() Get the extended attribute values, as a hashtable. @return The extended attribute values. **/ - Hashtable getExtendedAttributeValues() + Hashtable getExtendedAttributeValues() { - Hashtable results = new Hashtable(); + Hashtable results = new Hashtable(); // The offset to the start of the "optional/variable section" depends on the datastream level. int optionalSectionOffset = HEADER_LENGTH + get16bit(TEMPLATE_LENGTH_OFFSET);