/**
   * Run the file load file processors
   *
   * @param context DiskDeviceContext
   * @param state FileState
   * @param segment FileSegment
   */
  protected final void runFileLoadedProcessors(
      DiskDeviceContext context, FileState state, FileSegment segment) {

    // Check if there are any file processors configured

    if (m_fileProcessors == null || m_fileProcessors.numberOfProcessors() == 0) return;

    try {

      // Run all of the file load processors

      for (int i = 0; i < m_fileProcessors.numberOfProcessors(); i++) {

        // Get the current file processor

        FileProcessor fileProc = m_fileProcessors.getProcessorAt(i);

        // Run the file processor

        fileProc.processLoadedFile(context, state, segment);
      }

      // Make sure the file segment is closed after processing

      segment.closeFile();
    } catch (Exception ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) {
        Debug.println("$$ Load file processor exception");
        Debug.println(ex);
      }
    }
  }
  /**
   * File state cache is closing down, any resources attached to the file state must be released.
   *
   * @param state FileState
   */
  public void fileStateClosed(FileState state) {

    // DEBUG

    if (state == null) {
      Debug.println("%%%%% FileLoader.fileStateClosed() state=NULL %%%%%");
      return;
    }

    // Check if the file state has an associated file

    FileSegmentInfo segInfo = (FileSegmentInfo) state.findAttribute(DBFileSegmentInfo);

    if (segInfo != null
        && segInfo.isQueued() == false
        && segInfo.hasStatus() != FileSegmentInfo.SaveWait) {

      try {

        // Delete the temporary file

        segInfo.deleteTemporaryFile();

        // Debug

        if (Debug.EnableInfo && hasDebug())
          Debug.println("$$ Deleted temporary file " + segInfo.getTemporaryFile() + " [CLOSED] $$");
      } catch (IOException ex) {
      }
    }
  }
Beispiel #3
0
  /** Finalize, check if there are any deferred requests in the list */
  public void finalize() {
    if (m_deferredRequests != null && m_deferredRequests.size() > 0) {

      // Dump out the list of leaked deferred requests

      Debug.println("** Deferred requests found during per node finalize, perNode=" + this);

      for (DeferredRequest deferReq : m_deferredRequests)
        Debug.println("**  Leaked deferred request=" + deferReq);
    }
  }
Beispiel #4
0
  /**
   * Fail any deferred requests that are attached to this oplock, and clear the deferred list
   *
   * @return int Number of deferred requests that were failed
   */
  public int failDeferredRequests() {

    // Check if there are any deferred requests

    if (m_deferredRequests == null) return 0;

    int failCnt = 0;

    synchronized (m_deferredRequests) {
      for (DeferredRequest deferReq : m_deferredRequests) {

        // Get the deferred session/packet details

        SMBSrvSession sess = deferReq.getDeferredSession();
        SMBSrvPacket pkt = deferReq.getDeferredPacket();

        try {

          // Return an error for the deferred file open request

          if (sess.sendAsyncErrorResponseSMB(pkt, SMBStatus.NTAccessDenied, SMBStatus.NTErr)
              == true) {

            // Update the failed request count

            failCnt++;

            // DEBUG

            if (Debug.EnableDbg && sess.hasDebug(SMBSrvSession.DBG_OPLOCK))
              Debug.println("Oplock break timeout, oplock=" + this);
          } else if (Debug.EnableDbg && sess.hasDebug(SMBSrvSession.DBG_OPLOCK))
            Debug.println("Failed to send open reject, oplock break timed out, oplock=" + this);
        } catch (IOException ex) {

        } finally {

          // Make sure the packet is released back to the memory pool

          if (pkt != null) sess.getPacketPool().releasePacket(pkt);
        }
      }

      // Clear the deferred request list

      m_deferredRequests.clear();
    }

    // Return the count of failed requests

    return failCnt;
  }
  /**
   * Add a LANA listener
   *
   * @param lana int
   * @param l LanaListener
   */
  public final synchronized void addLanaListener(int lana, LanaListener l) {

    // Range check the LANA id

    if (lana < 0 || lana > 255) return;

    // Check if the listener array has been allocated

    if (m_listeners == null) {
      int len = LanaListenerArraySize;
      if (lana >= len) len = (lana + 4) & 0x00FC;

      m_listeners = new LanaListener[len];
    } else if (lana >= m_listeners.length) {

      // Extend the LANA listener array

      LanaListener[] newArray = new LanaListener[(lana + 4) & 0x00FC];

      // Copy the existing array to the extended array

      System.arraycopy(m_listeners, 0, newArray, 0, m_listeners.length);
      m_listeners = newArray;
    }

    // Add the LANA listener

    m_listeners[lana] = l;

    // DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println("[SMB] Win32 NetBIOS register listener for LANA " + lana);
  }
  /**
   * Process the set request
   *
   * @param rpc RpcPacket
   * @return RpcPacket
   */
  private final RpcPacket procSet(RpcPacket rpc) {

    //	Get the call parameters

    int progId = rpc.unpackInt();
    int verId = rpc.unpackInt();
    int proto = rpc.unpackInt();
    int port = rpc.unpackInt();

    //	DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println(
          "[PortMap] Set port program="
              + Rpc.getServiceName(progId)
              + ", version="
              + verId
              + ", protocol="
              + (proto == Rpc.TCP ? "TCP" : "UDP")
              + ", port="
              + port);

    //	Check if the port is already mapped

    PortMapping portMap = findPortMapping(progId, verId, proto);
    int portAdded = Rpc.False;

    if (portMap == null) {

      //	Add a mapping for the new service

      portMap = new PortMapping(progId, verId, proto, port);
      if (addPortMapping(portMap) == true) portAdded = Rpc.True;
    }

    //	Check if the service is on the same port as the current port mapping, and it is not
    //	an attempt to set the port mapper service port.

    else if (progId != PortMapper.ProgramId && portMap.getPort() == port) {

      //	Settings are the same as the existing service settings so accept it

      portAdded = Rpc.True;
    }

    //	Build the response header

    rpc.buildResponseHeader();

    //	Pack a boolean indicating if the port was added, or not

    rpc.packInt(portAdded);
    rpc.setLength();

    //	Return the response

    return rpc;
  }
  /**
   * Delete the specified file data
   *
   * @param fname String
   * @param fid int
   * @param stid int
   * @exception IOException
   */
  public void deleteFile(String fname, int fid, int stid) throws IOException {

    // Delete the file data from the database

    try {

      // Find the associated file state

      FileState fstate = m_stateCache.findFileState(fname, false);

      if (fstate != null) {

        // Get the file segment details

        FileSegmentInfo fileSegInfo = (FileSegmentInfo) fstate.removeAttribute(DBFileSegmentInfo);
        if (fileSegInfo != null) {
          try {

            // Change the file segment status

            fileSegInfo.setQueued(false);
            fileSegInfo.setStatus(FileSegmentInfo.Initial);

            // Delete the temporary file

            fileSegInfo.deleteTemporaryFile();
          } catch (Exception ex) {

            // DEBUG

            if (Debug.EnableInfo && hasDebug())
              Debug.println(
                  "## ObjIdLoader failed to delete temp file " + fileSegInfo.getTemporaryFile());
          }
        }
      }
    } catch (Exception ex) {

      // DEBUG

      if (Debug.EnableInfo && hasDebug())
        Debug.println("## ObjIdLoader deleteFile() error, " + ex.toString());
    }
  }
Beispiel #8
0
  /**
   * Decrement the file open count
   *
   * @return int
   */
  public final synchronized int decrementOpenCount() {

    //	Debug

    if (m_openCount <= 0)
      Debug.println("@@@@@ File close name=" + getPath() + ", count=" + m_openCount + " <<ERROR>>");
    else m_openCount--;

    return m_openCount;
  }
Beispiel #9
0
  /**
   * Requeue deferred requests to the thread pool for processing, oplock has been released
   *
   * @return int Number of deferred requests requeued
   */
  public int requeueDeferredRequests() {

    // Check if there are any deferred requests

    if (m_deferredRequests == null) return 0;

    int requeueCnt = 0;

    synchronized (m_deferredRequests) {
      for (DeferredRequest deferReq : m_deferredRequests) {

        // Get the deferred session/packet details

        SMBSrvSession sess = deferReq.getDeferredSession();
        SMBSrvPacket pkt = deferReq.getDeferredPacket();

        // DEBUG

        if (Debug.EnableDbg && sess.hasDebug(SMBSrvSession.DBG_OPLOCK))
          Debug.println(
              "Release oplock, queued deferred request to thread pool sess="
                  + sess.getUniqueId()
                  + ", pkt="
                  + pkt);

        try {

          // Queue the deferred request to the thread pool for processing

          sess.getThreadPool().queueRequest(new CIFSThreadRequest(sess, pkt));
        } catch (Throwable ex) {

          // Failed to queue the request to the thread pool, release the deferred packet back to the
          // memory pool

          sess.getPacketPool().releasePacket(pkt);
        }
      }

      // Clear the deferred request list

      m_deferredRequests.clear();
    }

    // Return the count of requeued requests

    return requeueCnt;
  }
Beispiel #10
0
  /** Static initializer used to load the native code library */
  static {

    // Check if we are running under 64 bit Windows

    String dllName = "Win32Utils";

    if (X64.isWindows64()) dllName = "Win32Utilsx64";

    //	Load the Win32 utils library

    try {
      System.loadLibrary(dllName);
    } catch (Exception ex) {
      Debug.println(ex);
    }
  }
  /**
   * Process the unset request
   *
   * @param rpc RpcPacket
   * @return RpcPacket
   */
  private final RpcPacket procUnSet(RpcPacket rpc) {

    //	Get the call parameters

    int progId = rpc.unpackInt();
    int verId = rpc.unpackInt();
    int proto = rpc.unpackInt();
    int port = rpc.unpackInt();

    //	DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println(
          "[PortMap] UnSet port program="
              + Rpc.getServiceName(progId)
              + ", version="
              + verId
              + ", protocol="
              + (proto == Rpc.TCP ? "TCP" : "UDP")
              + ", port="
              + port);

    //	Check if the port is mapped, and it is not an attempt to remove a portmapper portt

    PortMapping portMap = findPortMapping(progId, verId, proto);
    int portRemoved = Rpc.False;

    if (portMap != null && progId != PortMapper.ProgramId) {

      //	Remove the port mapping

      if (removePortMapping(portMap) == true) portRemoved = Rpc.True;
    }

    //	Build the response header

    rpc.buildResponseHeader();

    //	Pack a boolean indicating if the port was removed, or not

    rpc.packInt(portRemoved);
    rpc.setLength();

    //	Return the response

    return rpc;
  }
  protected final void shutdownHostAnnouncers() {
    if (m_hostAnnouncers.size() > 0) {

      for (int idx = 0; idx < m_hostAnnouncers.size(); idx++) {

        // Get the current host announcer

        HostAnnouncer hostAnnouncer = (HostAnnouncer) m_hostAnnouncers.get(idx);
        hostAnnouncer.shutdownAnnouncer();

        // DEBUG

        if (Debug.EnableInfo && hasDebug())
          Debug.println("[NetBIOS] Shutting down host announcer " + hostAnnouncer.getName());
      }
    }
  }
  /** Create a new temporary sub-directory */
  private final void createNewTempDirectory() {

    // Create the starting temporary sub-directory

    m_curTempName = m_tempDirName + getTempDirectoryPrefix() + m_curTempIdx++;
    m_curTempDir = new File(m_curTempName);

    if (m_curTempDir.exists() == false) m_curTempDir.mkdir();

    // Clear the temporary file count

    m_tempCount = 0;

    // DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println("ObjIdLoader Created new temp directory - " + m_curTempName);
  }
  /** Start the portmapper server */
  public void startServer() {

    try {

      //	Create the UDP RPC handler to accept incoming requests

      m_udpHandler =
          new UdpRpcDatagramHandler("PortMap", "Port", this, this, null, getPort(), MaxRequestSize);
      m_udpHandler.initializeSessionHandler(this);

      //	Start the UDP request listener is a seperate thread

      Thread udpThread = new Thread(m_udpHandler);
      udpThread.setName("PortMap_UDP");
      udpThread.start();

      //	Create the TCP RPC handler to accept incoming requests

      m_tcpHandler =
          new TcpRpcSessionHandler("PortMap", "Port", this, this, null, getPort(), MaxRequestSize);
      m_tcpHandler.initializeSessionHandler(this);

      //	Start the UDP request listener is a seperate thread

      Thread tcpThread = new Thread(m_tcpHandler);
      tcpThread.setName("PortMap_TCP");
      tcpThread.start();

      //	Add port mapper entries for the portmapper service

      PortMapping portMap =
          new PortMapping(PortMapper.ProgramId, PortMapper.VersionId, Rpc.UDP, getPort());
      addPortMapping(portMap);

      portMap = new PortMapping(PortMapper.ProgramId, PortMapper.VersionId, Rpc.TCP, getPort());
      addPortMapping(portMap);
    } catch (Exception ex) {
      Debug.println(ex);
    }
  }
  /**
   * Process the get port request
   *
   * @param rpc RpcPacket
   * @return RpcPacket
   */
  private final RpcPacket procGetPort(RpcPacket rpc) {

    //	Get the call parameters

    int progId = rpc.unpackInt();
    int verId = rpc.unpackInt();
    int proto = rpc.unpackInt();

    //	Find the required port mapping

    PortMapping portMap = findPortMapping(progId, verId, proto);

    //	DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println(
          "[PortMap] Get port program="
              + Rpc.getServiceName(progId)
              + ", version="
              + verId
              + ", protocol="
              + (proto == Rpc.TCP ? "TCP" : "UDP")
              + ", port="
              + (portMap != null ? portMap.getPort() : 0));

    //	Build the response header

    rpc.buildResponseHeader();

    //	Pack the port number of the requested RPC service, or zero if not found

    rpc.packInt(portMap != null ? portMap.getPort() : 0);
    rpc.setLength();

    //	Return the response

    return rpc;
  }
Beispiel #16
0
  /**
   * Add a deferred session/packet, whilst an oplock break is in progress
   *
   * @param deferredSess SMBSrvSession
   * @param deferredPkt SMBSrvPacket
   * @exception DeferFailedException If the session/packet cannot be deferred
   */
  public void addDeferredSession(SMBSrvSession deferredSess, SMBSrvPacket deferredPkt)
      throws DeferFailedException {

    // Allocate the deferred request list, if required

    if (m_deferredRequests == null) {
      synchronized (this) {
        if (m_deferredRequests == null)
          m_deferredRequests = new ArrayList<DeferredRequest>(MaxDeferredRequests);
      }
    }

    // Add the request to the list if there are spare slots

    synchronized (m_deferredRequests) {
      if (m_deferredRequests.size() < MaxDeferredRequests) {

        // Add the deferred request to the list

        m_deferredRequests.add(new DeferredRequest(deferredSess, deferredPkt));

        // Update the deferred processing count for the CIFS packet

        deferredPkt.incrementDeferredCount();

        // Set the time that the oplock break was sent to the client, if this is the first deferred
        // request

        if (m_deferredRequests.size() == 1) m_oplockBreakTime = System.currentTimeMillis();

        // DEBUG

        if (Debug.EnableDbg && deferredSess.hasDebug(SMBSrvSession.DBG_OPLOCK))
          Debug.println(
              "Added deferred request, list=" + m_deferredRequests.size() + ", oplock=" + this);
      } else throw new DeferFailedException("No more deferred slots available on oplock");
    }
  }
  /**
   * Process the dump request
   *
   * @param rpc RpcPacket
   * @return RpcPacket
   */
  private final RpcPacket procDump(RpcPacket rpc) {

    //	DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println("[PortMap] Dump ports request from " + rpc.getClientDetails());

    //	Build the response

    rpc.buildResponseHeader();

    //	Pack the active port mappings structures

    Enumeration enm = m_mappings.elements();

    while (enm.hasMoreElements()) {

      //	Get the current port mapping

      PortMapping portMap = (PortMapping) enm.nextElement();

      //	Pack the port mapping structure

      rpc.packInt(Rpc.True);
      rpc.packPortMapping(portMap);
    }

    //	Pack the end of list structure, set the response length

    rpc.packInt(Rpc.False);
    rpc.setLength();

    //	Return the response

    return rpc;
  }
  /** Thread method */
  public void run() {

    // Clear the shutdown flag

    m_shutdown = false;

    // If Winsock NetBIOS is not enabled then initialize the sockets interface

    ServerConfiguration config = m_server.getConfiguration();
    CIFSConfigSection cifsConfig =
        (CIFSConfigSection) config.getConfigSection(CIFSConfigSection.SectionName);

    if (cifsConfig.useWinsockNetBIOS() == false) {

      try {
        NetBIOSSocket.initializeSockets();
      } catch (WinsockNetBIOSException ex) {

        // DEBUG

        if (Debug.EnableError && hasDebug()) {
          Debug.println("[SMB] Win32 NetBIOS initialization error");
          Debug.println(ex);
        }

        // Shutdown the LANA monitor thread

        m_shutdown = true;
      }
    }

    // Loop until shutdown

    BitSet curLanas = new BitSet();

    while (m_shutdown == false) {

      // Wait for a network address change event

      Win32NetBIOS.waitForNetworkAddressChange();

      // Check if the monitor has been closed

      if (m_shutdown == true) continue;

      // Clear the current active LANA bit set

      curLanas.clear();

      // Get the available LANA list

      int[] lanas = Win32NetBIOS.LanaEnumerate();
      if (lanas != null) {

        // Check if there are any new LANAs available

        Win32NetBIOSSessionSocketHandler sessHandler = null;

        for (int i = 0; i < lanas.length; i++) {

          // Get the current LANA id, check if it's a known LANA

          int lana = lanas[i];
          curLanas.set(lana, true);

          if (m_lanas.get(lana) == false) {

            // DEBUG

            if (Debug.EnableInfo && hasDebug())
              Debug.println("[SMB] Win32 NetBIOS found new LANA, " + lana);

            // Create a single Win32 NetBIOS session handler using the specified LANA

            sessHandler = new Win32NetBIOSSessionSocketHandler(m_server, lana, hasDebug());

            try {
              sessHandler.initializeSessionHandler(m_server);
            } catch (Exception ex) {

              // DEBUG

              if (Debug.EnableError && hasDebug()) {
                Debug.println(
                    "[SMB] Win32 NetBIOS failed to create session handler for LANA " + lana);
                Debug.println(ex);
              }

              // Clear the session handler

              sessHandler = null;
            }

            // If the session handler was initialized successfully add it to the
            // SMB/CIFS server

            if (sessHandler != null) {

              // Add the session handler to the SMB/CIFS server

              //							m_server.addSessionHandler(sessHandler);

              // Run the NetBIOS session handler in a seperate thread

              Thread nbThread = new Thread(sessHandler);
              nbThread.setName("Win32NB_Handler_" + lana);
              nbThread.start();

              // DEBUG

              if (Debug.EnableInfo && hasDebug())
                Debug.println("[SMB] Win32 NetBIOS created session handler on LANA " + lana);

              // Check if a host announcer should be enabled

              if (cifsConfig.hasWin32EnableAnnouncer()) {

                // Create a host announcer

                Win32NetBIOSHostAnnouncer hostAnnouncer =
                    new Win32NetBIOSHostAnnouncer(
                        sessHandler,
                        cifsConfig.getDomainName(),
                        cifsConfig.getWin32HostAnnounceInterval());

                // Add the host announcer to the SMB/CIFS server list

                addHostAnnouncer(hostAnnouncer);
                hostAnnouncer.start();

                // DEBUG

                if (Debug.EnableInfo && hasDebug())
                  Debug.println("[SMB] Win32 NetBIOS host announcer enabled on LANA " + lana);
              }

              // Set the LANA in the available LANA list, and set the current status
              // to online

              m_lanas.set(lana);
              m_lanaSts.set(lana, true);
            }
          } else {

            // Check if the LANA has just come back online

            if (m_lanaSts.get(lana) == false) {

              // Change the LANA status to indicate the LANA is back online

              m_lanaSts.set(lana, true);

              // Inform the listener that the LANA is back online

              if (m_listeners != null && lana < m_listeners.length && m_listeners[lana] != null)
                m_listeners[lana].lanaStatusChange(lana, true);

              // DEBUG

              if (Debug.EnableError && hasDebug())
                Debug.println("[SMB] Win32 NetBIOS LANA online - " + lana);
            }
          }
        }

        // Check if there are any LANAs that have gone offline

        for (int i = 0; i < m_lanaSts.length(); i++) {

          if (curLanas.get(i) == false && m_lanaSts.get(i) == true) {

            // DEBUG

            if (Debug.EnableError && hasDebug())
              Debug.println("[SMB] Win32 NetBIOS LANA offline - " + i);

            // Change the LANA status

            m_lanaSts.set(i, false);

            // Check if there is an associated listener for the LANA

            if (m_listeners != null && m_listeners[i] != null) {

              // Notify the LANA listener that the LANA is now offline

              m_listeners[i].lanaStatusChange(i, false);
            }
          }
        }
      } else if (m_lanaSts.length() == 0) {

        // No network devices, sleep for a while as waitForNetworkAddressChange() does not
        // wait
        // if there are no network devices

        try {
          Thread.sleep(10000);
        } catch (InterruptedException e) {
        }
      }
    }
  }
  /**
   * Create a file segment to load/save the file data
   *
   * @param state FileState
   * @param params FileOpenParams
   * @param fname String
   * @param fid int
   * @param stid int
   * @param did int
   * @return CachedNetworkFile
   * @exception IOException
   */
  private final CachedNetworkFile createNetworkFile(
      FileState state, FileOpenParams params, String fname, int fid, int stid, int did)
      throws IOException {

    // The file state is used to synchronize the creation of the file segment as there may be other
    // sessions opening the file at the same time. We have to be careful that only one thread
    // creates the
    // file segment.

    FileSegment fileSeg = null;
    CachedNetworkFile netFile = null;

    synchronized (state) {

      // Check if the file segment has been attached to the file state

      FileSegmentInfo fileSegInfo = (FileSegmentInfo) state.findAttribute(DBFileSegmentInfo);
      if (fileSegInfo == null) {

        // Check if we need to create a new temporary sub-drectory

        if (m_tempCount++ >= m_tempMax) createNewTempDirectory();

        // Create a unique temporary file name

        StringBuffer tempName = new StringBuffer();
        tempName.append(getTempFilePrefix());
        tempName.append(fid);

        if (stid > 0) {
          tempName.append("_");
          tempName.append(stid);

          // DEBUG

          if (Debug.EnableInfo && hasDebug()) Debug.println("## Temp file for stream ##");
        }

        tempName.append(".tmp");

        // Create a new file segment

        fileSegInfo = new FileSegmentInfo();
        fileSeg =
            FileSegment.createSegment(
                fileSegInfo, tempName.toString(), m_curTempDir, params.isReadOnlyAccess() == false);

        // Add the segment to the file state cache

        state.addAttribute(DBFileSegmentInfo, fileSegInfo);

        // Check if the file is zero length, if so then set the file segment state to indicate it is
        // available

        DBFileInfo finfo = (DBFileInfo) state.findAttribute(FileState.FileInformation);
        if (finfo != null && finfo.getSize() == 0) fileSeg.setStatus(FileSegmentInfo.Available);
      } else {

        // Create the file segment to map to the existing temporary file

        fileSeg = new FileSegment(fileSegInfo, params.isReadOnlyAccess() == false);

        // Check if the temporary file exists, if not then create it

        File tempFile = new File(fileSeg.getTemporaryFile());
        if (tempFile.exists() == false) {

          // Create the temporary file

          tempFile.createNewFile();

          // Reset the file segment state to indicate a file load is required

          fileSeg.setStatus(FileSegmentInfo.Initial);
        }
      }

      // Create the new network file

      netFile =
          new CachedNetworkFile(
              fname, fid, stid, did, m_stateCache.getFileStateProxy(state), fileSeg, this);

      netFile.setGrantedAccess(
          params.isReadOnlyAccess() ? NetworkFile.READONLY : NetworkFile.READWRITE);
      netFile.setSequentialOnly(params.isSequentialAccessOnly());
      netFile.setAttributes(params.getAttributes());
      netFile.setFullName(params.getPath());

      if (stid != 0) netFile.setStreamName(params.getStreamName());
    }

    // Return the network file

    return netFile;
  }
  /**
   * Store a file
   *
   * @param req FileRequest
   * @return int
   * @exception Exception
   */
  public int storeFile(FileRequest req) throws Exception {

    // Check for a single file request

    int saveSts = StsError;
    SingleFileRequest saveReq = (SingleFileRequest) req;

    // Check if the temporary file still exists, if not then the file has been deleted from the
    // filesystem

    File tempFile = new File(saveReq.getTemporaryFile());
    FileSegment fileSeg = findFileSegmentForPath(saveReq.getVirtualPath());

    if (tempFile.exists() == false || fileSeg == null) {

      // DEBUG

      if (Debug.EnableInfo && hasDebug()) Debug.println("  Temporary file deleted");

      // Return an error status

      return StsError;
    }

    // Run any file store processors

    runFileStoreProcessors(m_dbCtx, saveReq.getFileState(), fileSeg);

    // Update the segment status, and clear the updated flag

    fileSeg.setStatus(FileSegmentInfo.Saving);
    fileSeg.getInfo().setUpdated(false);

    // Save the file data

    try {

      // Save the file data and get the assigned object id

      String objectId =
          saveFileData(saveReq.getFileId(), saveReq.getStreamId(), fileSeg, req.getAttributes());

      // Save the object id to the mapping database

      getDBObjectIdInterface().saveObjectId(saveReq.getFileId(), saveReq.getStreamId(), objectId);

      // Indicate that the save was successful

      saveSts = StsSuccess;
    } catch (DBException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file save failed

      saveSts = StsError;
    } catch (IOException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file save failed

      saveSts = StsError;
    }

    // Update the segment status

    if (saveSts == StsSuccess) fileSeg.setStatus(FileSegmentInfo.Saved, false);
    else fileSeg.setStatus(FileSegmentInfo.Error, false);

    // Return the data save status

    return saveSts;
  }
  /** Run the CIFS request */
  public void runRequest() {

    // Check if the session is still alive

    if (m_sess.isShutdown() == false) {

      SMBSrvPacket smbPkt = null;

      try {

        // Get the packet handler and read in the CIFS request

        WinsockNetBIOSPacketHandler pktHandler =
            (WinsockNetBIOSPacketHandler) m_sess.getPacketHandler();
        smbPkt = pktHandler.readPacket();

        // Clear the read in progress flag for the session

        m_sess.setReadInProgress(false);

        // DEBUG

        //				Debug.println("[SMB] Read packet, sess=" + m_sess.getUniqueId() + " ,pkt=" + smbPkt);

        // If the request packet is not valid then close the session

        if (smbPkt == null) {

          // Close the session

          m_sess.hangupSession("Client closed socket");
        } else {

          // Re-enable read events for this socket channel

          //					m_reqHandler.requeueSocketEvent( m_socketEvent,
          // pktHandler.getSocket().getSocket());
        }

        // Process the CIFS request

        m_sess.processPacket(smbPkt);
        smbPkt = null;

        // Process any asynchronous packets (oplock breaks and change notifications)

        int asyncCnt = m_sess.sendQueuedAsyncResponses();

        // DEBUG

        if (asyncCnt > 0 && Debug.EnableInfo && m_sess.hasDebug(SMBSrvSession.DBG_SOCKET))
          Debug.println(
              "Sent queued async packets (JNI/NIO) count="
                  + asyncCnt
                  + ", sess="
                  + m_sess.getUniqueId()
                  + ", addr="
                  + m_sess.getRemoteAddress().getHostAddress());
      } catch (Throwable ex) {
        Debug.println(ex);
      } finally {

        // Make sure the request packet is returned to the pool

        if (smbPkt != null) m_sess.getPacketPool().releasePacket(smbPkt);
      }
    }
  }
  /**
   * Close the network file
   *
   * @param sess SrvSession
   * @param netFile NetworkFile
   * @exception IOException
   */
  public void closeFile(SrvSession sess, NetworkFile netFile) throws IOException {

    // Close the cached network file

    if (netFile instanceof CachedNetworkFile) {

      // Get the cached network file

      CachedNetworkFile cacheFile = (CachedNetworkFile) netFile;
      cacheFile.closeFile();

      // Get the file segment details

      FileSegment fileSeg = cacheFile.getFileSegment();

      // Check if the file data has been updated, if so then queue a file save

      if (fileSeg.isUpdated() && netFile.hasDeleteOnClose() == false) {

        // Set the modified date/time and file size for the file

        File tempFile = new File(fileSeg.getTemporaryFile());

        netFile.setModifyDate(tempFile.lastModified());
        netFile.setFileSize(tempFile.length());

        // Queue a file save request to save the data back to the repository, if not already queued

        if (fileSeg.isSaveQueued() == false) {

          // Create a file save request for the updated file segment

          SingleFileRequest fileReq =
              new SingleFileRequest(
                  FileRequest.SAVE,
                  cacheFile.getFileId(),
                  cacheFile.getStreamId(),
                  fileSeg.getInfo(),
                  netFile.getFullName(),
                  cacheFile.getFileState());

          // Check if there are any attributes to be added to the file request

          if (hasRequiredAttributes() && sess != null) {

            // Check if the user name is required

            if (m_reqAttributes.containsString(FileRequest.AttrUserName)) {

              // Add the user name attribute

              ClientInfo cInfo = sess.getClientInformation();
              String userName = "";

              if (cInfo != null && cInfo.getUserName() != null) userName = cInfo.getUserName();

              fileReq.addAttribute(new NameValue(FileRequest.AttrUserName, userName));
            }

            // Check if the protocol type is required

            if (m_reqAttributes.containsString(FileRequest.AttrProtocol)) {

              // Add the protocol type attribute

              fileReq.addAttribute(new NameValue(FileRequest.AttrProtocol, sess.getProtocolName()));
            }
          }

          // Set the file segment status

          fileSeg.setStatus(FileSegmentInfo.SaveWait, true);

          // Queue the file save request

          queueFileRequest(fileReq);
        } else if (Debug.EnableInfo && hasDebug()) {

          // DEBUG

          Debug.println("## FileLoader Save already queued for " + fileSeg);
        }
      }

      // Update the cache timeout for the temporary file if there are no references to the file. If
      // the file was
      // opened for sequential access only it will be expired quicker.

      else if (cacheFile.getFileState().getOpenCount() == 0) {

        // If the file was opened for sequential access only then we can delete it from the
        // temporary area
        // sooner

        long tmo = System.currentTimeMillis();

        if (cacheFile.isSequentialOnly()) tmo += SequentialFileExpire;
        else tmo += m_stateCache.getFileStateExpireInterval();

        // Set the file state expiry, the local file data will be deleted when the file state
        // expires (if there
        // are still no references to the file).

        cacheFile.getFileState().setExpiryTime(tmo);
      }

      // If the database is not online and the file is marked for delete then queue a delete file
      // request to do
      // the
      // delete when the database is back online

      if (m_dbCtx.isAvailable() == false && netFile.hasDeleteOnClose()) {

        // Queue an offline delete request for the file

        DeleteFileRequest deleteReq =
            new DeleteFileRequest(
                cacheFile.getFileId(),
                cacheFile.getStreamId(),
                fileSeg.getTemporaryFile(),
                cacheFile.getFullNameStream(),
                cacheFile.getFileState());
        m_dbCtx.addOfflineFileDelete(deleteReq);

        // DEBUG

        if (Debug.EnableInfo && hasDebug())
          Debug.println("## FileLoader queued offline delete, " + deleteReq);
      }
    }
  }
  /**
   * Create a network file for the specified file
   *
   * @param params FileOpenParams
   * @param fid int
   * @param stid int
   * @param did int
   * @param create boolean
   * @param dir boolean
   * @exception IOException
   * @exception FileNotFoundException
   */
  public NetworkFile openFile(
      FileOpenParams params, int fid, int stid, int did, boolean create, boolean dir)
      throws IOException, FileNotFoundException {

    // Split the file name to get the name only

    String[] paths = FileName.splitPath(params.getPath());
    String name = paths[1];

    if (params.isStream()) name = name + params.getStreamName();

    // Find, or create, the file state for the file/directory

    FileState fstate = m_stateCache.findFileState(params.getFullPath(), true);
    fstate.setExpiryTime(System.currentTimeMillis() + m_stateCache.getFileStateExpireInterval());

    // Check if the file is a directory

    DBNetworkFile netFile = null;

    if (dir == false) {

      // Create the network file and associated file segment

      CachedNetworkFile cacheFile = createNetworkFile(fstate, params, name, fid, stid, did);
      netFile = cacheFile;

      // Check if the file is being opened for sequential access and the data has not yet been
      // loaded

      FileSegment fileSeg = cacheFile.getFileSegment();

      if (create == true || params.isOverwrite() == true) {

        // Indicate that the file data is available, this is a new file or the existing file is
        // being
        // overwritten so there is no data to load.

        fileSeg.setStatus(FileSegmentInfo.Available);
      } else if (params.isSequentialAccessOnly() && fileSeg.isDataLoading() == false) {

        synchronized (cacheFile.getFileState()) {

          // Create the temporary file

          cacheFile.openFile(create);
          cacheFile.closeFile();

          // Queue a file data load request

          if (fileSeg.isDataLoading() == false)
            queueFileRequest(
                new SingleFileRequest(
                    FileRequest.LOAD,
                    cacheFile.getFileId(),
                    cacheFile.getStreamId(),
                    fileSeg.getInfo(),
                    cacheFile.getFullName(),
                    fstate));
        }

        // DEBUG

        if (Debug.EnableInfo && hasDebug())
          Debug.println(
              "ObjIdLoader Queued file load, SEQUENTIAL access, file=" + cacheFile.getFullName());
      }
    } else {

      // Create a placeholder network file for the directory

      netFile = new DirectoryNetworkFile(name, fid, did, m_stateCache.getFileStateProxy(fstate));

      // Debug

      if (Debug.EnableInfo && hasDebug())
        Debug.println("ObjIdLoader.openFile() DIR name=" + name + ", state=" + fstate);
    }

    // Return the network file

    return netFile;
  }
  /**
   * Start the file loader
   *
   * @param ctx DeviceContext
   */
  public void startLoader(DeviceContext ctx) {

    // Get the file state cache from the context

    m_stateCache = getContext().getStateCache();

    // Add the file loader as a file state listener so that we can cleanup temporary data files

    m_stateCache.addStateListener(this);

    // Get the database interface

    DBQueueInterface dbQueue = null;

    if (getContext().getDBInterface() instanceof DBQueueInterface)
      dbQueue = (DBQueueInterface) getContext().getDBInterface();
    else throw new RuntimeException("Database interface does not implement queue interface");

    // Perform a queue cleanup before starting the thread pool. This will check the temporary cache
    // area and delete
    // files that are not part of a queued save/transaction save request.

    FileRequestQueue recoveredQueue = null;

    try {

      // Cleanup the temporary cache area and queue

      recoveredQueue =
          dbQueue.performQueueCleanup(m_tempDir, TempDirPrefix, TempFilePrefix, JarFilePrefix);

      // DEBUG

      if (recoveredQueue != null && Debug.EnableInfo && hasDebug())
        Debug.println(
            "[DBLoader] Cleanup recovered "
                + recoveredQueue.numberOfRequests()
                + " pending save files");
    } catch (DBException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);
    }

    // Check if there are any file save requests pending in the queue database

    FileRequestQueue saveQueue = new FileRequestQueue();

    try {
      dbQueue.loadFileRequests(1, FileRequest.SAVE, saveQueue, 1);
    } catch (DBException ex) {
    }

    // Create the background load/save thread pool

    m_backgroundLoader = new BackgroundLoadSave("DBLdr", dbQueue, m_stateCache, this);

    m_backgroundLoader.setReadWorkers(m_readWorkers);
    m_backgroundLoader.setWriteWorkers(m_writeWorkers);

    m_backgroundLoader.setDebug(m_threadDebug);

    // Start the file loader threads, start the request loading if there are pending file save
    // requests

    m_backgroundLoader.startThreads(saveQueue.numberOfRequests());

    // Queue the recovered file save requests

    if (recoveredQueue != null) {

      // Queue the file save requests

      while (recoveredQueue.numberOfRequests() > 0)
        queueFileRequest(recoveredQueue.removeRequestNoWait());
    }
  }
  /**
   * File state has expired. The listener can control whether the file state is removed from the
   * cache, or not.
   *
   * @param state FileState
   * @return true to remove the file state from the cache, or false to leave the file state in the
   *     cache
   */
  public boolean fileStateExpired(FileState state) {

    // Check if the file state has an associated file segment

    FileSegmentInfo segInfo = (FileSegmentInfo) state.findAttribute(DBFileSegmentInfo);
    boolean expire = true;

    if (segInfo != null) {

      // Check if the file has a request queued

      if (segInfo.isQueued() == false) {

        try {

          // Delete the temporary file and reset the segment status so that the data may be loaded
          // again
          // if required.

          if (segInfo.hasStatus() != FileSegmentInfo.Initial) {

            // Delete the temporary file

            try {
              segInfo.deleteTemporaryFile();
            } catch (IOException ex) {

              // DEBUG

              if (Debug.EnableError) {
                Debug.println("Delete temp file error: " + ex.toString());
                File tempFile = new File(segInfo.getTemporaryFile());
                Debug.println(
                    "  TempFile file="
                        + tempFile.getAbsolutePath()
                        + ", exists="
                        + tempFile.exists());
                Debug.println("  FileState state=" + state);
                Debug.println("  FileSegmentInfo segInfo=" + segInfo);
                Debug.println("  StateCache size=" + m_stateCache.numberOfStates());
              }
            }

            // Remove the file segment, reset the file segment back to the initial state

            state.removeAttribute(DBFileSegmentInfo);
            segInfo.setStatus(FileSegmentInfo.Initial);

            // Reset the file state to indicate file data load required

            state.setDataStatus(FileState.FILE_LOADWAIT);

            // Check if the temporary file sub-directory is now empty, and it is not the current
            // temporary
            // sub-directory

            if (segInfo.getTemporaryFile().startsWith(m_curTempName) == false) {

              // Check if the sub-directory is empty

              File tempFile = new File(segInfo.getTemporaryFile());
              File subDir = tempFile.getParentFile();
              String[] files = subDir.list();
              if (files == null || files.length == 0) {

                // Delete the sub-directory

                subDir.delete();

                // DEBUG

                if (Debug.EnableInfo && hasDebug())
                  Debug.println(
                      "$$ Deleted temporary directory "
                          + subDir.getPath()
                          + ", curTempName="
                          + m_curTempName
                          + ", tempFile="
                          + segInfo.getTemporaryFile());
              }
            }

            // Indicate that the file state should not be deleted

            expire = false;

            // Debug

            if (Debug.EnableInfo && hasDebug())
              Debug.println(
                  "$$ Deleted temporary file " + segInfo.getTemporaryFile() + " [EXPIRED] $$");
          }

          // If the file state is not to be deleted reset the file state expiration timer

          if (expire == false)
            state.setExpiryTime(
                System.currentTimeMillis() + m_stateCache.getFileStateExpireInterval());
        } catch (Exception ex) {

          // DEBUG

          if (Debug.EnableError) {
            Debug.println("$$  " + ex.toString());
            Debug.println("  state=" + state);
          }
        }
      } else {

        // DEBUG

        if (hasDebug()) {
          File tempFile = new File(segInfo.getTemporaryFile());
          if (tempFile.exists() == false) {
            Debug.println("== Skipped file state, queued " + state);
            Debug.println("   File seg=" + segInfo);
          }
        }

        // File state is queued, do not expire

        expire = false;
      }
    } else if (state.isDirectory()) {

      // Nothing to do when it's a directory, just allow it to expire

      expire = true;
    } else if (Debug.EnableInfo && hasDebug()) Debug.println("$$ Expiring state=" + state);

    // Return true if the file state can be expired

    return expire;
  }
  /**
   * Load a file
   *
   * @param req FileRequest
   * @return int
   * @exception Exception
   */
  public int loadFile(FileRequest req) throws Exception {

    // DEBUG

    long startTime = 0L;
    SingleFileRequest loadReq = (SingleFileRequest) req;

    if (Debug.EnableInfo && hasDebug()) {
      Debug.println(
          "## ObjIdLoader loadFile() req="
              + loadReq.toString()
              + ", thread="
              + Thread.currentThread().getName());
      startTime = System.currentTimeMillis();
    }

    // Check if the temporary file still exists, if not then the file has been deleted from the
    // filesystem

    File tempFile = new File(loadReq.getTemporaryFile());
    FileSegment fileSeg = findFileSegmentForPath(loadReq.getVirtualPath());

    if (tempFile.exists() == false || fileSeg == null) {

      // DEBUG

      if (Debug.EnableInfo && hasDebug()) Debug.println("  Temporary file deleted");

      // Return an error status

      fileSeg.setStatus(FileSegmentInfo.Error, false);
      return StsError;
    }

    // DEBUG

    if (Debug.EnableInfo && hasDebug())
      Debug.println(
          "## ObjIdLoader fileSeg="
              + fileSeg.getTemporaryFile()
              + ", virtPath="
              + loadReq.getVirtualPath());

    // Load the file data

    int loadSts = StsRequeue;
    String objectId = null;

    int fileId = loadReq.getFileId();
    int strmId = loadReq.getStreamId();

    try {

      // Update the segment status

      fileSeg.setStatus(FileSegmentInfo.Loading);

      // Get the object id for the file

      objectId = getDBObjectIdInterface().loadObjectId(fileId, strmId);

      if (objectId != null) {

        // Load the file data

        loadFileData(fileId, strmId, objectId, fileSeg);

        // Set the load status

        loadSts = StsSuccess;

        // DEBUG

        if (Debug.EnableInfo && hasDebug()) {
          long endTime = System.currentTimeMillis();
          Debug.println(
              "## ObjIdLoader loaded fid="
                  + loadReq.getFileId()
                  + ", stream="
                  + loadReq.getStreamId()
                  + ", time="
                  + (endTime - startTime)
                  + "ms");
        }
      } else {

        // DEBUG

        if (Debug.EnableInfo && hasDebug())
          Debug.println(
              "## ObjIdLoader No object id mapping for fid="
                  + loadReq.getFileId()
                  + ", stream="
                  + loadReq.getStreamId());

        // Indicate a load success

        loadSts = StsSuccess;
      }
    } catch (DBException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsError;
    } catch (FileOfflineException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsError;
    } catch (IOException ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsRequeue;
    } catch (Exception ex) {

      // DEBUG

      if (Debug.EnableError && hasDebug()) Debug.println(ex);

      // Indicate the file load failed

      loadSts = StsError;
    }

    // Clear the last modified date/time of the temporary file to indicate it has not been updated

    tempFile.setLastModified(0L);

    // Check if the file was loaded successfully

    if (loadSts == StsSuccess) {

      // Signal that the file data is available

      fileSeg.signalDataAvailable();

      // Update the file status

      fileSeg.setStatus(FileSegmentInfo.Available, false);

      // Run the file load processors

      runFileLoadedProcessors(getContext(), loadReq.getFileState(), fileSeg);
    } else if (loadSts == StsError) {

      // Set the file status to indicate error to any client reading threads

      fileSeg.setStatus(FileSegmentInfo.Error, false);

      // Wakeup any threads waiting on data for this file

      fileSeg.setReadableLength(0L);
      fileSeg.signalDataAvailable();

      // Delete the temporary file

      fileSeg.deleteTemporaryFile();
    }

    // Return the load file status

    return loadSts;
  }
  /**
   * Process a \PIPE\LANMAN transaction request.
   *
   * @param tbuf Transaction setup, parameter and data buffers
   * @param sess SMB server session that received the transaction.
   * @param trans Packet to use for reply
   * @return true if the transaction has been handled, else false.
   * @exception java.io.IOException The exception description.
   * @exception org.alfresco.aifs.smb.SMBSrvException The exception description.
   */
  public static final boolean processRequest(
      TransactBuffer tbuf, SMBSrvSession sess, SMBSrvPacket trans)
      throws IOException, SMBSrvException {

    // Create a transaction packet

    SMBSrvTransPacket tpkt = new SMBSrvTransPacket(trans.getBuffer());

    // Get the transaction command code, parameter descriptor and data descriptor strings from
    // the parameter block.

    DataBuffer paramBuf = tbuf.getParameterBuffer();

    int cmd = paramBuf.getShort();
    String prmDesc = paramBuf.getString(false);
    String dataDesc = paramBuf.getString(false);

    // Debug

    if (Debug.EnableInfo && sess.hasDebug(SMBSrvSession.DBG_IPC))
      sess.debugPrintln(
          "\\PIPE\\LANMAN\\ transact request, cmd="
              + cmd
              + ", prm="
              + prmDesc
              + ", data="
              + dataDesc);

    // Call the required transaction handler

    boolean processed = false;

    switch (cmd) {

        // Share

      case PacketType.RAPShareEnum:
        processed = procNetShareEnum(sess, tbuf, prmDesc, dataDesc, tpkt);
        break;

        // Get share information

      case PacketType.RAPShareGetInfo:
        processed = procNetShareGetInfo(sess, tbuf, prmDesc, dataDesc, tpkt);
        break;

        // Workstation information

      case PacketType.RAPWkstaGetInfo:
        processed = procNetWkstaGetInfo(sess, tbuf, prmDesc, dataDesc, tpkt);
        break;

        // Server information

      case PacketType.RAPServerGetInfo:
        processed = procNetServerGetInfo(sess, tbuf, prmDesc, dataDesc, tpkt);
        break;

        // Print queue information

      case PacketType.NetPrintQGetInfo:
        processed = procNetPrintQGetInfo(sess, tbuf, prmDesc, dataDesc, tpkt);
        break;

        // No handler

      default:

        // Debug

        if (Debug.EnableInfo && sess.hasDebug(SMBSrvSession.DBG_IPC))
          sess.debugPrintln(
              "No handler for \\PIPE\\LANMAN\\ request, cmd="
                  + cmd
                  + ", prm="
                  + prmDesc
                  + ", data="
                  + dataDesc);
        break;
    }

    // Check if the transaction packet has allocated an associated packet from the pool, we need to
    // copy the associated packet
    // to the outer request packet so that it is released back to the pool.

    if (tpkt.hasAssociatedPacket()) {

      Debug.println(
          "[SMB] PipeLanManHandler allocated associated packet, len="
              + tpkt.getAssociatedPacket().getBufferLength());

      // Copy the associated packet to the outer request packet

      trans.setAssociatedPacket(tpkt.getAssociatedPacket());
      tpkt.setAssociatedPacket(null);
    }

    // Return the transaction processed status

    return processed;
  }