gt:             long len = is.available();
context: /*
   * This file is part of Splice Machine.
   * Splice Machine is free software: you can redistribute it and/or modify it under the terms of the
   * GNU Affero General Public License as published by the Free Software Foundation, either
   * version 3, or (at your option) any later version.
   * Splice Machine is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
   * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
   * See the GNU Affero General Public License for more details.
   * You should have received a copy of the GNU Affero General Public License along with Splice Machine.
   * If not, see <http://www.gnu.org/licenses/>.
   *
   * Some parts of this source code are based on Apache Derby, and the following notices apply to
   * Apache Derby:
   *
   * Apache Derby is a subproject of the Apache DB project, and is licensed under
   * the Apache License, Version 2.0 (the "License"); you may not use these files
   * except in compliance with the License. You may obtain a copy of the License at:
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software distributed
   * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   * CONDITIONS OF ANY KIND, either express or implied. See the License for the
   * specific language governing permissions and limitations under the License.
   *
   * Splice Machine, Inc. has modified the Apache Derby code in this file.
   *
   * All such Splice Machine modifications are Copyright 2012 - 2020 Splice Machine, Inc.,
   * and are licensed to you under the GNU Affero General Public License.
   */
  
  package com.splicemachine.db.impl.io;
  
  import com.splicemachine.db.io.StorageFactory;
  import com.splicemachine.db.io.StorageFile;
  import com.splicemachine.db.io.StorageRandomAccessFile;
  
  import com.splicemachine.db.iapi.services.sanity.SanityManager;
  
  import java.io.File;
  import java.io.InputStream;
  import java.io.OutputStream;
  import java.io.IOException;
  import java.io.FileNotFoundException;
  import java.net.MalformedURLException;
  import java.net.URL;
  import com.splicemachine.db.iapi.error.StandardException;
  
  /**
   * This class provides the base for read-only stream implementations of the StorageFile interface. It is used with the
   * classpath, jar, http, and https subsubprotocols
   */
  abstract class InputStreamFile implements StorageFile
  {
  
      final String path;
      final int nameStart; // getName() = path.substring( nameStart)
      final BaseStorageFactory storageFactory;
  
      InputStreamFile( BaseStorageFactory storageFactory, String path)
      {
          this.storageFactory = storageFactory;
          if( path == null || path.isEmpty())
          {
              this.path = storageFactory.dataDirectory;
              nameStart = -1;
          }
          else
          {
              StringBuilder sb = new StringBuilder( storageFactory.separatedDataDirectory);
              if( File.separatorChar != '/')
                  sb.append( path.replace( File.separatorChar, '/'));
              else
                  sb.append( path);
              this.path = sb.toString();
              nameStart = this.path.lastIndexOf( '/') + 1;
          }
      }
  
      InputStreamFile( BaseStorageFactory storageFactory, String parent, String name)
      {
          this.storageFactory = storageFactory;
          StringBuilder sb = new StringBuilder( storageFactory.separatedDataDirectory);
          if( File.separatorChar != '/')
          {
              sb.append( parent.replace( File.separatorChar, '/'));
              sb.append( '/');
              sb.append( name.replace( File.separatorChar, '/'));
          }
          else
          {
              sb.append( parent);
              sb.append( '/');
              sb.append( name);
          }
          path = sb.toString();
          nameStart = this.path.lastIndexOf( '/') + 1;
      }
  
      InputStreamFile( InputStreamFile dir, String name)
      {
          this.storageFactory = dir.storageFactory;
          StringBuilder sb = new StringBuilder( dir.path);
          sb.append( '/');
          if( File.separatorChar != '/')
              sb.append( name.replace( File.separatorChar, '/'));
          else
              sb.append( name);
          path = sb.toString();
          nameStart = this.path.lastIndexOf( '/') + 1;
      }
  
      InputStreamFile( BaseStorageFactory storageFactory, String child, int pathLen)
      {
          this.storageFactory = storageFactory;
          path = child.substring( 0, pathLen);
          nameStart = this.path.lastIndexOf( '/') + 1;
      }
  
      public boolean equals( Object other)
      {
          if( other == null || ! getClass().equals( other.getClass()))
              return false;
          InputStreamFile otherFile = (InputStreamFile) other;
          return path.equals( otherFile.path);
      }
  
      public int hashCode()
      {
          return path.hashCode();
      }
  
      /**
       * Get the names of all files and sub-directories in the directory named by this path name.
       *
       * @return An array of the names of the files and directories in this
       *         directory denoted by this abstract pathname. The returned array will have length 0
       *         if this directory is empty. Returns null if this StorageFile is not  a directory, or
       *         if an I/O error occurs.
       */
      public String[] list()
      {
          return null;
      }
  
      /**
       * Determine whether the named file is writable.
       *
       * @return <b>true</b> if the file exists and is writable, <b>false</b> if not.
       */
      public boolean canWrite()
      {
          return false;
      }
  
      /**
       * Tests whether the named file exists.
       *
       * @return <b>true</b> if the named file exists, <b>false</b> if not.
       */
      public abstract boolean exists();
  
      /**
       * Tests whether the named file is a directory, or not. This is only called in writable storage factories.
       *
       * @return <b>true</b> if named file exists and is a directory, <b>false</b> if not.
       *          The return value is undefined if the storage is read-only.
       */
      public boolean isDirectory()
      {
          return false;
      }
  
      /**
       * Deletes the named file or empty directory. This method does not delete non-empty directories.
       *
       * @return <b>true</b> if the named file or directory is successfully deleted, <b>false</b> if not
       */
      public boolean delete()
      {
          return false;
      }
  
      /**
       * Deletes the named file and, if it is a directory, all the files and directories it contains.
       *
       * @return <b>true</b> if the named file or directory is successfully deleted, <b>false</b> if not
       */
      public boolean deleteAll()
      {
          return false;
      }
  
      /**
       * Converts this StorageFile into a pathname string. The character returned by StorageFactory.getSeparator()
       * is used to separate the directory and file names in the sequence.
       *
       *<p>
       *<b>The returned path may include the database directory. Therefore it cannot be directly used to make an StorageFile
       * equivalent to this one.</b>
       *
       * @return The pathname as a string.
       *
       * @see StorageFactory#getSeparator
       */
      public String getPath()
      {
          if( File.separatorChar != '/')
              return path.replace( '/', File.separatorChar);
          return path;
      } // end of getPath
  
      public String getCanonicalPath() throws IOException
      {
          return storageFactory.getCanonicalName() + "/" + path;
      }
      
      /**
       * @return The last segment in the path name, "" if the path name sequence is empty.
       */
      public String getName()
      {
          return (nameStart < 0) ? "" : path.substring( nameStart);
      }
  
      /**
       * If the named file does not already exist then create it as an empty normal file.
       *
       * The implementation
       * must synchronize with other threads accessing the same file (in the same or a different process).
       * If two threads both attempt to create a file with the same name
       * at the same time then at most one should succeed.
       *
       * @return <b>true</b> if this thread's invocation of createNewFile successfully created the named file;
       *         <b>false</b> if not, i.e. <b>false</b> if the named file already exists or if another concurrent thread created it.
       *
       * @exception IOException - If the directory does not exist or some other I/O error occurred
       */
      public boolean createNewFile() throws IOException
      {
          throw new IOException( "createNewFile called in a read-only file system.");
      }
  
      /**
       * Rename the file denoted by this name. Note that StorageFile objects are immutable. This method
       * renames the underlying file, it does not change this StorageFile object. The StorageFile object denotes the
       * same name as before, however the exists() method will return false after the renameTo method
       * executes successfully.
       *
       *<p>It is not specified whether this method will succeed if a file already exists under the new name.
       *
       * @param newName the new name.
       *
       * @return <b>true</b> if the rename succeeded, <b>false</b> if not.
       */
      public boolean renameTo( StorageFile newName)
      {
          return false;
      }
      
      /**
       * Creates the named directory.
       *
       * @return <b>true</b> if the directory was created; <b>false</b> if not.
       */
      public boolean mkdir()
      {
          return false;
      }
  
      /**
       * Creates the named directory, and all nonexistent parent directories.
       *
       * @return <b>true</b> if the directory was created, <b>false</b> if not
       */
      public boolean mkdirs()
      {
          return false;
      }
  
      /**
       * Returns the length of the named file if it is not a directory. The return value is not specified
       * if the file is a directory.
       *
       * @return The length, in bytes, of the named file if it exists and is not a directory,
       *         0 if the file does not exist, or any value if the named file is a directory.
       */
      public long length()
      {
          try
          {
              InputStream is = getInputStream();
              if( is == null)
                  return 0;
  