org.alfresco.jlan.server.filesys.db.DBDiskLDriver.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.jlan.server.filesys.db.DBDiskLDriver.java

Source

/*
 * Copyright (C) 2006-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 */

package org.alfresco.jlan.server.filesys.db;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;

import org.alfresco.jlan.debug.Debug;
import org.alfresco.jlan.locking.LockConflictException;
import org.alfresco.jlan.server.SrvSession;
import org.alfresco.jlan.server.core.DeviceContext;
import org.alfresco.jlan.server.core.DeviceContextException;
import org.alfresco.jlan.server.filesys.AccessDeniedException;
import org.alfresco.jlan.server.filesys.DiskDeviceContext;
import org.alfresco.jlan.server.filesys.DiskFullException;
import org.alfresco.jlan.server.filesys.DiskInterface;
import org.alfresco.jlan.server.filesys.DiskOfflineException;
import org.alfresco.jlan.server.filesys.DiskSizeInterface;
import org.alfresco.jlan.server.filesys.DiskVolumeInterface;
import org.alfresco.jlan.server.filesys.FileAccessToken;
import org.alfresco.jlan.server.filesys.FileAttribute;
import org.alfresco.jlan.server.filesys.FileExistsException;
import org.alfresco.jlan.server.filesys.FileIdInterface;
import org.alfresco.jlan.server.filesys.FileInfo;
import org.alfresco.jlan.server.filesys.FileName;
import org.alfresco.jlan.server.filesys.FileNameException;
import org.alfresco.jlan.server.filesys.FileOfflineException;
import org.alfresco.jlan.server.filesys.FileOpenParams;
import org.alfresco.jlan.server.filesys.FileSharingException;
import org.alfresco.jlan.server.filesys.FileStatus;
import org.alfresco.jlan.server.filesys.FileType;
import org.alfresco.jlan.server.filesys.NetworkFile;
import org.alfresco.jlan.server.filesys.SearchContext;
import org.alfresco.jlan.server.filesys.SecurityDescriptorInterface;
import org.alfresco.jlan.server.filesys.SrvDiskInfo;
import org.alfresco.jlan.server.filesys.SymbolicLinkInterface;
import org.alfresco.jlan.server.filesys.TreeConnection;
import org.alfresco.jlan.server.filesys.VolumeInfo;
import org.alfresco.jlan.server.filesys.cache.FileState;
import org.alfresco.jlan.server.filesys.cache.FileStateCache;
import org.alfresco.jlan.server.filesys.cache.LocalFileAccessToken;
import org.alfresco.jlan.server.filesys.db.mysql.MySQLDBLInterface;
import org.alfresco.jlan.server.filesys.loader.FileSegment;
import org.alfresco.jlan.server.filesys.loader.FileSegmentInfo;
import org.alfresco.jlan.server.filesys.loader.NamedFileLoader;
import org.alfresco.jlan.server.filesys.quota.QuotaManager;
import org.alfresco.jlan.server.locking.FileLockingInterface;
import org.alfresco.jlan.server.locking.LockManager;
import org.alfresco.jlan.server.locking.OpLockInterface;
import org.alfresco.jlan.server.locking.OpLockManager;
import org.alfresco.jlan.smb.SharingMode;
import org.alfresco.jlan.smb.WinNT;
import org.alfresco.jlan.smb.nt.SecurityDescriptor;
import org.alfresco.jlan.smb.server.SMBSrvException;
import org.alfresco.jlan.smb.server.ntfs.NTFSStreamsInterface;
import org.alfresco.jlan.smb.server.ntfs.StreamInfo;
import org.alfresco.jlan.smb.server.ntfs.StreamInfoList;
import org.alfresco.jlan.util.MemorySize;
import org.alfresco.jlan.util.WildCard;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.extensions.config.ConfigElement;

import com.run.VirtuaServerSyncServlet;
import com.util.CacheManagerUtil;
import com.util.DBUtil;
import com.util.DiskUtil;

/**
 * Database Disk Driver Class
 * 
 * @author gkspencer/LUO KAI
 */
public class DBDiskLDriver implements DiskInterface, DiskSizeInterface, DiskVolumeInterface, NTFSStreamsInterface,
        FileLockingInterface, FileIdInterface, SymbolicLinkInterface, OpLockInterface, SecurityDescriptorInterface {

    private Logger log4j = Logger.getLogger(this.getClass());
    // Attributes attached to the file state

    public static final String DBStreamList = "DBStreamList";

    // Default mode values for files/folders, if not specified in the
    // file/folder create parameters

    public static final int DefaultNFSFileMode = 0644;
    public static final int DefaultNFSDirMode = 0755;

    // Maximum file name length

    public static final int MaxFileNameLen = 255;

    // Maximum timestamp value to allow for file timestamps (01-Jan-2030
    // 00:00:00)

    public static final long MaxTimestampValue = 1896134400000L;

    // Enable/disable debug output

    private boolean m_debug = false;

    /**
     * Close the specified file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param file
     *            Network file details
     * @exception IOException
     */
    public void closeFile(SrvSession sess, TreeConnection tree, NetworkFile file) throws IOException {

        // if (file.getFileSize() == 0 && !file.isDirectory()) {
        // log4j.error("DBD#closeFile() filesize == 0 !");
        // return;
        // }
        // Access the database context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        String shareName = DiskUtil.findFristPath(file.getFullName(), tree.getFullName());
        // String shareName = tree.getContext().getShareName();
        // Check if the file is an NTFS stream

        if (file.isStream()) {

            // Close the NTFS stream

            closeStream(sess, tree, file);

            // Check if the stream is marked for deletion

            if (file.hasDeleteOnClose())
                deleteStream(sess, tree, file.getFullNameStream());
            return;
        }

        // Debug
        // log4j.debug("DBD#closeFile() file=" + file.getFullName());

        // Close the file

        boolean force = file.isForce();
        boolean archived = file.isArchived();
        boolean closed2 = file.isClosed();
        boolean readOnly = file.isReadOnly();

        dbCtx.getFileLoader().closeFile(sess, file);

        file.setClosed(true);

        // Access the JDBC file

        DBNetworkFile jdbcFile = null;

        String sharePath = DiskUtil.findFristPath(file.getFullName());
        if (file instanceof DBNetworkFile) {

            // Access the JDBC file

            jdbcFile = (DBNetworkFile) file;

            // Decrement the open file count

            FileState fstate = jdbcFile.getFileState();

            // Check if the file state is valid, if not then check the main file
            // state cache

            if (fstate == null) {

                // Check the main file state cache

                fstate = getFileState(file.getFullName(), dbCtx, false);

                String name = file.getName();
                //            if (name.startsWith("nsh")&& name.endsWith(".docx")) {
                //               log4j.debug("DBDiskLDriver.closeFile()1111 " + name);
                //            }
                // DEBUG
                // log4j.debug("** Last file close, no file state for " +
                // file.getFullName());
            } else {
                // If the file open count is now zero then reset the stored
                // sharing mode

                int openCount = fstate.getOpenCount();
                LocalFileAccessToken token = (LocalFileAccessToken) file.getAccessToken();

                if (token != null) {
                    boolean attributesOnly = token.isAttributesOnly();
                    int fileAccess = dbCtx.getStateCache().releaseFileAccess(fstate, token);

                    if (!fstate.isDirectory()) {

                        DBFileInfo finfo = getFileDetails(fstate.getPath(), dbCtx, fstate, userName, shareName,
                                userId);
                        boolean b = true;//finfo.isLocked();
                        if (b && !fstate.isDirectory()) {// ?locked??locked??

                            if (!fstate.isDirectory() && !file.getName().startsWith("~$")) {
                                String ext = DiskUtil.getExt(fstate.getPath());
                                if (DBUtil.SUPPORT_EXT.contains(ext) && !ext.equals("txt")) {
                                    boolean rele = token.isReleased();

                                    int sharedAccess = fstate.getSharedAccess();
                                    boolean isWps = false;
                                    if (ext.equals("ppt") || ext.equals("xls") || ext.equals("doc")) {
                                        isWps = true;
                                        if (openCount == 1 && !attributesOnly) {
                                        }
                                        if (sharedAccess != 7) {
                                            log4j.debug("DBDiskLDriver.closeFile() sharedAccess != 7 ");
                                        }
                                        if (openCount == 2) {
                                            log4j.debug("DBDiskLDriver.closeFile() openCount == 2 ");
                                        }
                                    }

                                    if ((!attributesOnly && openCount == 0) || isWps
                                            || (attributesOnly && openCount == 0 && (ext.endsWith("docx")
                                                    || ext.endsWith("xlsx") || ext.endsWith("pptx")))) {

                                        CachedNetworkFile cacheFile = (CachedNetworkFile) file;

                                        FileSegmentInfo info = cacheFile.getFileSegment().getInfo();
                                        String g = file.getGrantedAccessAsString();
                                        int status2 = ((DBNetworkFile) file).getStatus();

                                        boolean available = info.isDataAvailable();
                                        long readableLength = info.getReadableLength();
                                        int writeCount = file.getWriteCount();
                                        int status = info.hasStatus();
                                        boolean queued = info.isQueued();
                                        boolean updated = info.isUpdated();

                                        FileSegment fileSeg = cacheFile.getFileSegment();

                                        if (ext.equals("ppt")) {// 2010 wps ppt
                                            if (

                                            // 1 ???
                                            (available && queued == false && updated == false && status == 3
                                                    && writeCount == 0 && openCount == 1 && status2 == 0
                                                    && g.equals("ReadOnly"))// && status2 == 0 

                                                    ||

                                                    // 2 ??:wps?5s?
                                                    (available && !queued && !updated && status == 3
                                                            && writeCount == 0 && openCount == 1 && status2 == 2
                                                            && g.equals("ReadOnly"))) {//

                                                //                           available && readableLength>0 && status == 3 
                                                //                              && queued == false && status2 == 0 && g.equals("ReadOnly")) {
                                                //                           if (available && readableLength>0 && queued == false) {
                                                MySQLDBLInterface f = (MySQLDBLInterface) dbCtx.getDBInterface();
                                                f.releaseLock(userId, fstate.getFileId(), shareName);
                                                //                              log4j.debug("DBDiskLDriver.closeFile()");
                                            }
                                        } else if (ext.equals("doc")) {// wps wps doc
                                            if (
                                            // 1 ???
                                            (available && queued == false && updated == false // && status == 3 
                                                    && writeCount == 0 && openCount == 1 && g.equals("WriteOnly"))// && status2 == 0 

                                                    ||

                                                    // 2 ??
                                                    (!available && !queued && !updated && status == 0
                                                            && writeCount == 0 && openCount == 1 && status2 == 2
                                                            && g.equals("WriteOnly"))//

                                            )

                                            {
                                                MySQLDBLInterface f = (MySQLDBLInterface) dbCtx.getDBInterface();
                                                f.releaseLock(userId, fstate.getFileId(), shareName);
                                            }
                                        } else if (ext.equals("xls")) {// wps wps xls
                                            if (
                                            // 1 ???
                                            (available && queued == false && updated == false && status == 3
                                                    && writeCount == 0 && openCount == 1 && g.equals("WriteOnly"))// && status2 == 0 

                                                    ||

                                                    // 2 ??
                                                    (!available && !queued && !updated && status == 0
                                                            && writeCount == 0 && openCount == 1
                                                            && g.equals("WriteOnly"))//  && status2 == 2

                                            ) {
                                                MySQLDBLInterface f = (MySQLDBLInterface) dbCtx.getDBInterface();
                                                f.releaseLock(userId, fstate.getFileId(), shareName);
                                            }
                                        } else {

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

                                            } else {

                                                if (available && readableLength > 0 && status == 3
                                                        && queued == false) {
                                                    MySQLDBLInterface f = (MySQLDBLInterface) dbCtx
                                                            .getDBInterface();
                                                    f.releaseLock(userId, fstate.getFileId(), shareName);
                                                }
                                            }

                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                if (openCount > 1) {

                    // DEBUG
                    log4j.debug("** Last file close, reset shared access for " + file.getFullName() + ", state="
                            + fstate);

                } else {
                    // log4j.debug("** File close, file=" + file.getFullName() +
                    // ", openCount=" + fstate.getOpenCount());
                }

                // Check if there is an oplock on the file

                if (jdbcFile.hasOpLock()) {

                    // Release the oplock

                    OpLockInterface flIface = (OpLockInterface) this;
                    OpLockManager oplockMgr = flIface.getOpLockManager(sess, tree);

                    oplockMgr.releaseOpLock(jdbcFile.getOpLock().getPath());

                    // DEBUG
                    log4j.debug("Released oplock for closed file, file=" + jdbcFile.getFullName());
                }

                // Clear the access token

                file.setAccessToken(null);
            }

            // Release any locks on the file owned by this session

            if (jdbcFile.hasLocks()) {

                // Get the lock manager

                FileLockingInterface flIface = (FileLockingInterface) this;
                LockManager lockMgr = flIface.getLockManager(sess, tree);

                // DEBUG
                log4j.debug("Releasing locks for closed file, file=" + jdbcFile.getFullName() + ", locks="
                        + jdbcFile.numberOfLocks());

                // Release all locks on the file owned by this session

                lockMgr.releaseLocksForFile(sess, tree, file);
            }

            // Check if we have a valid file state

            if (fstate != null) {

                // Update the cached file size

                DBFileInfo finfo = (DBFileInfo) fstate.findAttribute(FileState.FileInformation);
                if (finfo != null && file.getWriteCount() > 0) {

                    // Update the file size

                    finfo.setSize(jdbcFile.getFileSize());

                    // Update the modified date/time

                    finfo.setModifyDateTime(jdbcFile.getModifyDate());

                    // DEBUG
                    log4j.debug(
                            "  File size=" + jdbcFile.getFileSize() + ", modifyDate=" + jdbcFile.getModifyDate());
                }

                // DEBUG
                // log4j.debug("  Open count=" +
                // jdbcFile.getFileState().getOpenCount());
            }

            // Check if the file/directory is marked for delete

            /**
             * doc~$file.hasDeleteOnClose()true?false?
             *   file.getName().startsWith("~1111$") && || (file.getWriteCount()==0)
             */
            if (file.hasDeleteOnClose()) {

                boolean b = true;

                if (file.hasDeleteOnClose() || b) {

                    // Check for a file or directory
                    if (sharePath.equals(DBUtil.SHARENAME_RECIVEFILE)) {
                        // if(shareName.equals(DBUtil.SHARENAME_RECIVEFILE)){
                        log4j.warn(DBUtil.SHARENAME_RECIVEFILE + "   ?? ? path:"
                                + file.getFullName());
                        throw new AccessDeniedException("??");
                    } else {
                        try {
                            if (file.isDirectory())
                                deleteDirectory(sess, tree, file.getFullName());
                            else
                                deleteFile(sess, tree, file.getFullName());
                        } catch (AccessDeniedException ex) {
                            throw new AccessDeniedException("??");
                        }
                    }
                    // DEBUG
                    log4j.debug(
                            "  Marked for delete ,fileId:" + file.getFileId() + " ,fullName:" + file.getFullName());
                }
            }
        } else {
            log4j.error("DBD#closeFile() DBNetworkFile fileId:" + file.getFileId() + ", file=" + file);
        }

        // Check if the file was opened for write access, if so then update the
        // file size and modify date/time

        if (file.getGrantedAccess() != NetworkFile.READONLY && file.isDirectory() == false
                && file.getWriteCount() > 0) {

            // DEBUG
            log4j.debug("  Update file size=" + file.getFileSize());

            // Get the current date/time

            long modifiedTime = 0L;
            if (file.hasModifyDate())
                modifiedTime = file.getModifyDate();
            else
                modifiedTime = System.currentTimeMillis();

            // Check if the modified time is earlier than the file creation
            // date/time

            if (file.hasCreationDate() && modifiedTime < file.getCreationDate()) {

                // Use the creation date/time for the modified date/time

                modifiedTime = file.getCreationDate();

                // DEBUG
                log4j.debug("Close file using creation date/time for modified date/time");
            }
        }
    }

    /**
     * Create a new directory
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param params
     *            Directory create parameters
     * @exception IOException
     */
    public void createDirectory(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException {

        // Access the database context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        // String shareName = tree.getContext().getShareName();
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        String ipAddress = sess.getClientInformation().getClientAddress();
        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false)
            throw new DiskOfflineException("Database is offline");

        // Get, or create, a file state for the new path. Initially this will
        // indicate that the directory
        // does not exist.

        FileState fstate = getFileState(params.getPath(), dbCtx, false);
        String shareName = DiskUtil.findFristPath(params.getPath(), tree.getFullName());
        if (fstate != null && fstate.fileExists() == true) {
            log4j.error("Path " + params.getPath() + " exists");
            throw new FileExistsException("Path " + params.getPath() + " exists");
        }

        // If there is no file state check if the directory exists

        if (fstate == null) {

            // Create a file state for the new directory

            fstate = getFileState(params.getPath(), dbCtx, true);

            // Get the file details for the directory

            if (getFileDetails(params.getPath(), dbCtx, fstate, userName, shareName, userId) != null) {
                log4j.error("Path " + params.getPath() + " exists");
                throw new FileExistsException("Path " + params.getPath() + " exists");
            }
        }

        // Find the parent directory id for the new directory

        int dirId = findParentDirectoryId(dbCtx, params.getPath(), true, userName, shareName, userId);
        if (dirId == -1) {
            log4j.error("Cannot find parent directory path:" + params.getPath());
            throw new IOException("Cannot find parent directory");
        }

        // Create the new directory entry

        FileAccessToken accessToken = null;
        int fid = -1;

        try {

            // Get the directory name

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

            // ??
            if (StringUtils.isEmpty(paths[0]) || "\\".equals(paths[0])) {
                log4j.warn("DBD#createFile() ?? , path:" + params.getPath()
                        + " ,path[0]:" + paths[0]);
                throw new AccessDeniedException("??");
            } else if (shareName.equals(DBUtil.SHARENAME_COMMFILE)
                    || shareName.equals(DBUtil.SHARENAME_COMMFILE_ALIAS)) {
                // ?
                if (("\\" + shareName).equalsIgnoreCase(paths[0])) {
                    log4j.warn("DBD#createFile() ??, path:"
                            + params.getPath());
                    throw new AccessDeniedException("??");
                }
            } else if (shareName.equalsIgnoreCase(DBUtil.SHARENAME_RECIVEFILE)) {
                log4j.warn("??");
                throw new AccessDeniedException("??");
            }

            // Check if the directory name is too long

            if (dname != null && dname.length() > MaxFileNameLen) {
                log4j.error("Directory name too long, " + dname);
                throw new FileNameException("Directory name too long, " + dname);
            }

            // If retention is enabled check if the file is a temporary folder

            boolean retain = true;

            if (dbCtx.hasRetentionPeriod()) {

                // Check if the file is marked delete on close

                if (params.isDeleteOnClose())
                    retain = false;
            }

            // Set the default NFS file mode, if not set

            if (params.hasMode() == false)
                params.setMode(DefaultNFSDirMode);

            // Make sure the create directory option is enabled

            if (params.hasCreateOption(WinNT.CreateDirectory) == false) {
                log4j.error("Create directory called for non-directory");
                throw new IOException("Create directory called for non-directory");
            }

            // Check if the file can be opened in the requested mode
            //
            // Note: The file status is set to NotExist at this point, the file
            // record creation may fail

            accessToken = dbCtx.getStateCache().grantFileAccess(params, fstate, FileStatus.NotExist);

            // Use the database interface to create the new file record

            fid = dbCtx.getDBInterface().createFileRecord(dname, dirId, params, retain, userName, shareName,
                    ipAddress);

            // Indicate that the path exists

            fstate.setFileStatus(FileStatus.DirectoryExists, FileState.ReasonFolderCreated);

            // Set the file id for the new directory

            fstate.setFileId(fid);

            // If retention is enabled get the expiry date/time

            if (dbCtx.hasRetentionPeriod() && retain == true) {
                RetentionDetails retDetails = dbCtx.getDBInterface().getFileRetentionDetails(dirId, fid);
                if (retDetails != null)
                    fstate.setRetentionExpiryDateTime(retDetails.getEndTime());
            }

            // Check if the file loader handles create directory requests

            if (fid != -1 && dbCtx.getFileLoader() instanceof NamedFileLoader) {

                // Create the directory in the filesystem/repository

                NamedFileLoader namedLoader = (NamedFileLoader) dbCtx.getFileLoader();
                namedLoader.createDirectory(params.getPath(), fid);
            }

            // Release the access token

            if (accessToken != null) {
                dbCtx.getStateCache().releaseFileAccess(fstate, accessToken);
                accessToken = null;
            }
        } catch (DBException ex) {
            log4j.error("createDirectory error", ex);
            throw new IOException();
        } finally {

            // Check if the file is not valid but an access token has been
            // allocated

            if (fid == -1 && accessToken != null)
                dbCtx.getStateCache().releaseFileAccess(fstate, accessToken);
        }
    }

    /**
     * Create a new file entry
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param params
     *            FileOpenParams
     * @return NetworkFile
     */
    public NetworkFile createFile(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException {
        // Access the database context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        String ipAddress = sess.getClientInformation().getClientAddress();
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Database is offline");
            throw new DiskOfflineException("Database is offline");
        }

        // Check if this is a stream create

        FileState fstate = getFileState(params.getPath(), dbCtx, true);
        log4j.debug("DBD#createFile(); params.isStream:" + params.isStream() + " ; path:" + params.getPath());

        if (params.isStream()) {

            // Make sure the parent file exists

            if (fileExists(sess, tree, params.getPath()) == FileStatus.FileExists) {

                // Create a new stream associated with the existing file

                return createStream(sess, tree, params, fstate, dbCtx);
            } else {

                // Parent file does not exist
                log4j.error("Parent file does not exist to create stream, " + params.getPath());
                throw new FileNotFoundException("Parent file does not exist to create stream, " + params.getPath());
            }
        } else if (fstate.fileExists()) {

            // File already exists
            log4j.error("File exists, " + params.getPath());
            throw new FileExistsException("File exists, " + params.getPath());
        }

        // Split the path string and find the directory id to attach the file to
        String shareName = DiskUtil.findFristPath(params.getPath(), tree.getFullName());
        int dirId = findParentDirectoryId(dbCtx, params.getPath(), true, userName, shareName, userId);
        if (dirId == -1)// ?? ??ParentDirectoryId ??
        {
            log4j.error("Cannot find parent directory path:" + params.getPath());
            throw new IOException("Cannot find parent directory");
        }

        // Check if the allocation size for the new file is greater than the
        // maximum allowed file size

        if (dbCtx.hasMaximumFileSize() && params.getAllocationSize() > dbCtx.getMaximumFileSize()) {
            log4j.error("Required allocation greater than maximum file size");
            throw new DiskFullException("Required allocation greater than maximum file size");
        }

        // Create a new file

        DBNetworkFile file = null;
        FileAccessToken accessToken = null;

        try {

            // Get the file name

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

            // Check if the file name is too long

            if (fname != null && fname.length() > MaxFileNameLen) {
                log4j.error("File name too long, " + fname);
                throw new FileNameException("File name too long, " + fname);
            }

            if (StringUtils.isEmpty(paths[0]) || "\\".equals(paths[0])) {
                log4j.warn("DBD#createFile() ?? , path:" + params.getPath()
                        + " ,path[0]:" + paths[0]);
                throw new AccessDeniedException("??");
            } else if (shareName.equals(DBUtil.SHARENAME_COMMFILE)
                    || shareName.equals(DBUtil.SHARENAME_COMMFILE_ALIAS)) {
                // ?
                if (("\\" + shareName).equalsIgnoreCase(paths[0])) {
                    log4j.warn(
                            "DBD#createFile() ??, path:" + params.getPath());
                    throw new AccessDeniedException("??");
                }
            } else if (shareName.equalsIgnoreCase(DBUtil.SHARENAME_RECIVEFILE)) {
                log4j.warn("??");
                throw new AccessDeniedException("??");
            }
            // If retention is enabled check if the file is a temporary file

            boolean retain = true;

            if (dbCtx.hasRetentionPeriod()) {

                // Check if the file is marked delete on close

                if (params.isDeleteOnClose())
                    retain = false;
            }

            // Set the default NFS file mode, if not set

            if (params.hasMode() == false)
                params.setMode(DefaultNFSFileMode);

            // Check if the current file open allows the required shared access

            if (params.getPath().equals("\\") == false) {

                // Check if the file can be opened in the requested mode
                //
                // Note: The file status is set to NotExist at this point, the
                // file record creation may fail

                int filestate = FileStatus.FileExists;
                // if (fname.endsWith("docx") ||
                // (fname.toLowerCase().startsWith("~") &&
                // fname.endsWith("tmp"))) {
                // filestate = FileStatus.NotExist;
                // } else {
                //
                // }
                accessToken = dbCtx.getStateCache().grantFileAccess(params, fstate, FileStatus.NotExist);
            }
            int fid = dbCtx.getDBInterface().createFileRecord(fname, dirId, params, retain, userName, shareName,
                    ipAddress);
            // Create a new file record

            // Indicate that the file exists

            fstate.setFileStatus(FileStatus.FileExists, FileState.ReasonFileCreated);

            // Save the file id

            fstate.setFileId(fid);

            // If retention is enabled get the expiry date/time

            if (dbCtx.hasRetentionPeriod() && retain == true) {
                RetentionDetails retDetails = dbCtx.getDBInterface().getFileRetentionDetails(dirId, fid);
                if (retDetails != null)
                    fstate.setRetentionExpiryDateTime(retDetails.getEndTime());
            }

            // Create a network file to hold details of the new file entry

            file = (DBNetworkFile) dbCtx.getFileLoader().openFile(params, fid, 0, dirId, true, false, userName);
            file.setFullName(params.getPath());
            file.setDirectoryId(dirId);
            file.setAttributes(params.getAttributes());
            file.setFileState(dbCtx.getStateCache().getFileStateProxy(fstate));

            file.setAccessToken(accessToken);

            // Open the file

            file.openFile(true);
        } catch (DBException ex) {

            // Remove the file state for the new file

            dbCtx.getStateCache().removeFileState(fstate.getPath());

            // DEBUG
            log4j.error("Create file error: ", ex);
        } finally {

            // Check if the file is not valid but an access token has been
            // allocated

            if (file == null && accessToken != null)
                dbCtx.getStateCache().releaseFileAccess(fstate, accessToken);
        }

        // Return the new file details

        if (file == null) {
            log4j.error("Failed to create file " + params.getPath());
            throw new IOException("Failed to create file " + params.getPath());
        }
        return file;
    }

    /**
     * Delete a directory
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param dir
     *            Path of directory to delete
     * @exception IOException
     */
    public void deleteDirectory(SrvSession sess, TreeConnection tree, String dir) throws IOException {

        // Debug
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        String shareName = DiskUtil.findFristPath(dir);
        String ipAddress = sess.getClientInformation().getClientAddress();
        log4j.debug("DBD#deleteDirectory() dir=" + dir);

        // Access the JDBC context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        if (shareName.equalsIgnoreCase(DBUtil.SHARENAME_RECIVEFILE)) {
            log4j.warn(shareName + " ,??");
            throw new AccessDeniedException("??");
        } else if (dir.indexOf("\\", 2) == -1) {
            log4j.warn(dir + " ,??");
            throw new AccessDeniedException("??");
        } else {
            // ?
            // Check if the database is online

            if (dbCtx.getDBInterface().isOnline() == false) {
                log4j.error("Database is offline");
                throw new DiskOfflineException("Database is offline");
            }

            // Get the file state for the path

            FileState fstate = getFileState(dir, dbCtx, false);
            if (fstate != null && fstate.fileExists() == false) {
                log4j.error("Path does not exist, " + dir);
                throw new FileNotFoundException("Path does not exist, " + dir);
            }

            // Create a file state if it does not exist

            if (fstate == null)
                fstate = getFileState(dir, dbCtx, true);

            // Get the directory details

            DBFileInfo dinfo = getFileDetails(dir, dbCtx, fstate, userName, shareName, userId);
            if (dinfo == null) {
                log4j.error("dinfo does not exist, " + dir);
                throw new FileNotFoundException(dir);
            }
            if (dinfo.isReadOnly()) {
                log4j.warn(dir + " ,?");
                throw new AccessDeniedException("?");
            }

            // Check if the directory contains any files
            try {

                // Check if the file loader handles delete directory requests.
                // Called first as the loader may throw an exception
                // to stop the directory being deleted.

                if (dbCtx.isTrashCanEnabled() == false && dbCtx.getFileLoader() instanceof NamedFileLoader) {

                    // Delete the directory in the filesystem/repository

                    NamedFileLoader namedLoader = (NamedFileLoader) dbCtx.getFileLoader();
                    namedLoader.deleteDirectory(dir, dinfo.getFileId());
                }

                // Delete the directory file record, or mark as deleted if the
                // trashcan is enabled

                dbCtx.getDBInterface().deleteFileRecord(dinfo.getDirectoryId(), dinfo.getFileId(),
                        dbCtx.isTrashCanEnabled(), userName, shareName, ipAddress);

                // Indicate that the path does not exist

                fstate.setFileStatus(FileStatus.NotExist, FileState.ReasonFolderDeleted);
                fstate.setFileId(-1);
                fstate.removeAttribute(FileState.FileInformation);
            } catch (DBException ex) {
                log4j.error("DBException:", ex);
                throw new IOException();
            }
        }
    }

    /**
     * Delete a file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param name
     *            Name of file to delete
     * @exception IOException
     */
    public void deleteFile(SrvSession sess, TreeConnection tree, String name) throws IOException {
        // boolean isWord2003tmp = name.startsWith("~") &&
        // name.toLowerCase().endsWith(".tmp");
        // log4j.debug( "deleteFile : " + name);
        // if (name.endsWith("doc") || name.endsWith("docx")) {// || name.endsWith("tmp")) {
        // TODO HOW to deal with wps ? wpsdoc?
        //  Excel PowerPoint
        //log4j.warn( "Delete file will not execute : " + name);
        //return ;
        // }
        // Access the JDBC context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        String ipAddress = sess.getClientInformation().getClientAddress();
        // String shareName = tree.getContext().getShareName();
        String shareName = DiskUtil.findFristPath(name, tree.getFullName());

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        if (shareName.equalsIgnoreCase(DBUtil.SHARENAME_RECIVEFILE)) {
            log4j.warn(shareName + ", ??");
            throw new AccessDeniedException("??");
        }
        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Database is offline");
            throw new DiskOfflineException("Database is offline");
        }

        // Check if the file name is a stream

        if (FileName.containsStreamName(name)) {

            // Delete a stream within a file

            deleteStream(sess, tree, name);
            return;
        }

        String ext = DiskUtil.getExt(name);// ?
        // Get the file state for the path

        FileState fstate = getFileState(name, dbCtx, false);
        if (fstate != null && fstate.fileExists() == false) {
            if (StringUtils.isEmpty(ext) || "tmp".equals(ext)) {
                log4j.warn("DBD#deleteFile() ? File does not exist  name:" + name);
                // return ;// ?
            } else {
                log4j.warn("File does not exist, " + name);
            }
            throw new FileNotFoundException("File does not exist, " + name); //?,????/?
        }

        // Create a file state for the file, if not already valid

        if (fstate == null)
            fstate = getFileState(name, dbCtx, true);

        DBFileInfo dbInfo = null;

        try {

            // Check if the file is within an active retention period

            getRetentionDetailsForState(dbCtx, fstate);

            if (fstate.hasActiveRetentionPeriod()) {
                throw new AccessDeniedException("File retention active");
            }

            // Get the file details
            dbInfo = getFileDetails(name, dbCtx, fstate, userName, shareName, userId);

            String fname = null;
            if (dbInfo == null) {
                throw new FileNotFoundException(name);
            }

            // DEBUG
            log4j.debug("DBD#deleteFile() name=" + name + ", state=" + fstate);
            /*
             * ??(office??) start int userId = 0; try{ UserBean user
             * = dbCtx.getDBInterface().getUserByUsername(userName); if(null !=
             * user) { userId = user.getId(); } } catch (DBException ex) {
             * log4j.
             * error("User is not exist from database, userId:"+userId+" username:"
             * +userName,ex); throw new
             * AccessDeniedException("User is not exist from database, userId:"
             * +userId+" username:"+userName,ex); } DBFileInfo finfo =
             * dbCtx.getDBInterface().getFileInformation(0, fstate.getFileId(),
             * DBInterface.FileNameOnly,userId,shareName,userName); if(null !=
             * finfo) { String[] paths = FileName.splitPath( name); String fname
             * = paths[1]; if(!finfo.getFileName().equalsIgnoreCase(fname)) {
             * //????? return ;//??? } if(finfo.isReadOnly()) {
             * log4j.warn(fstate.getFileId()+" ,?"); throw new
             * AccessDeniedException("?"); } }
             */
            /* ??(office??) end */
            // Delete the file in the filesystem/repository, the loader may
            // prevent the file delete by throwing
            // an exception
            // boolean canDel = !"xls".equals(ext);//xls??,xls2003
            boolean canDel = (!"xls".equals(ext) && !"xlsx".equals(ext))
                    && (!"ppt".equals(ext) && !"pptx".equals(ext)) && (!"doc".equals(ext) && !"docx".equals(ext));// xls??,xls2003
            if (dbCtx.isTrashCanEnabled() == false)
                ;// && canDel)
            dbCtx.getFileLoader().deleteFile(name, fstate.getFileId(), 0, shareName);

            // If the file is a symbolic link delete the symbolic link record

            if (null != dbInfo && dbInfo.isFileType() == FileType.SymbolicLink && canDel)
                dbCtx.getDBInterface().deleteSymbolicLinkRecord(dbInfo.getDirectoryId(), fstate.getFileId());

            /*
             * ???WPS?doc? (Office2007 // Check if the
             * file has any NTFS streams
             */
            StreamInfoList streamList = getStreamList(sess, tree, name);

            if (streamList != null && streamList.numberOfStreams() > 0) {

                // Make a copy of the streams list as streams are removed from
                // the original list as we delete them

                StreamInfoList sList = new StreamInfoList(streamList);

                // Delete the streams

                StringBuilder sPath = new StringBuilder(256);
                sPath.append(name);
                int delCnt = 0;

                for (int idx = 0; idx < sList.numberOfStreams(); idx++) {

                    // Get the current stream details

                    StreamInfo sInfo = sList.getStreamAt(idx);
                    if (sInfo.getName().equals(FileName.MainDataStreamName) == false) {

                        // Build the full path to the stream

                        sPath.setLength(name.length());
                        sPath.append(sInfo.getName());

                        // Delete the stream

                        deleteStream(sess, tree, sPath.toString());
                        delCnt++;
                    }
                }

                // DEBUG

                if (delCnt > 0)
                    log4j.debug("DBD#deleted " + delCnt + " streams for name=" + name);
            }

            // Delete the file record

            if (null != dbInfo && null != fstate) {
                dbCtx.getDBInterface().deleteFileRecord(dbInfo.getDirectoryId(), fstate.getFileId(),
                        dbCtx.isTrashCanEnabled(), userName, shareName, ipAddress);
            }

            // Indicate that the path does not exist
            /* ????????? */
            // if("tmp".equals(ext)|| "xls".equals(ext))
            // if("tmp".equals(ext)|| "xls".equals(ext) || "xlsx".equals(ext) ||
            // (name.startsWith("~$") && "doc".equals(ext)) ||
            // (name.startsWith("~$") && "docx".equals(ext)))
            // if("tmp".equals(ext) || "xls".equals(ext) ||
            // (name.startsWith("~$") &&
            // "doc".equals(ext))||(name.startsWith("~$") &&
            // "xlsx".equals(ext)))
            // if("tmp".equals(ext) || "xls".equals(ext) ||
            // (name.startsWith("~$") && "doc".equals(ext))
            // ||(name.startsWith("~$") && "xlsx".equals(ext))
            // ||("pptx".equals(ext) ||"ppt".equals(ext)))
            // //name.startsWith("~$") &&
            // {
            fstate.setFileStatus(FileStatus.NotExist, FileState.ReasonFileDeleted);
            fstate.setFileId(-1);
            //         log4j.warn("DBD#deleteFile() FileStatus.NotExist-fstate.setFileId(-1): name"
            //               + name + " , ext:" + ext);
            // }
            fstate.removeAttribute(FileState.FileInformation);

            // Check if there is a quota manager, if so then release the file
            // space

            if (dbCtx.hasQuotaManager() && null != dbInfo) {

                // Release the file space back to the filesystem free space

                dbCtx.getQuotaManager().releaseSpace(sess, tree, fstate.getFileId(), null, dbInfo.getSize());
            }
        } catch (DBException ex) {
            log4j.error("Failed to delete file " + name);
            throw new IOException("Failed to delete file " + name);
        }
    }

    /**
     * Check if the specified file exists, and it is a file.
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param name
     *            File name
     * @return int
     */
    public int fileExists(SrvSession sess, TreeConnection tree, String name) {
        // Access the JDBC context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        String shareName = DiskUtil.findFristPath(name, tree.getFullName());
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        // debug
        // log4j.debug("DBD#fileExists name=" + name);
        // Check if the path contains an NTFS stream name

        int fileSts = FileStatus.NotExist;
        FileState fstate = null;

        // Check for the root directory
        if (name.length() == 0 || name.compareTo("\\") == 0) {
            // log4j.debug("DBD#fileExists  ,  DirectoryExists ");
            return FileStatus.DirectoryExists;
        }
        if (name.equalsIgnoreCase("\\" + userName + DBUtil.SPECIAL_CHAR)) {
            // log4j.debug("DBD#fileExists username@ ,  DirectoryExists ");
            return FileStatus.DirectoryExists;
        }
        // if(name.toLowerCase().startsWith("\\"+userName.toLowerCase()+DBUtil.SPECIAL_CHAR)==false)
        // {
        // log4j.warn("DBD#fileExists ??name? ,  NotExist ; name:"+name);
        // return FileStatus.NotExist;
        // }
        // (:username@/; username@/)
        if (name.equalsIgnoreCase("\\" + shareName)) {
            if (shareName.equalsIgnoreCase(DBUtil.SHARENAME_USERFILE)
                    || shareName.equalsIgnoreCase(DBUtil.SHARENAME_RECIVEFILE)
                    || shareName.equalsIgnoreCase(DBUtil.SHARENAME_COMMFILE)
                    || shareName.equalsIgnoreCase(DBUtil.SHARENAME_COMMFILE_ALIAS)) {
                return FileStatus.DirectoryExists;
            }
        }

        if (FileName.containsStreamName(name)) {

            // Get the file information for the stream
            FileInfo fInfo = null;
            try {
                fInfo = getFileInformation(sess, tree, name);
            } catch (IOException ex) {
                log4j.error(ex);
            }

            // Check if the file information was retrieved for the stream
            if (fInfo != null)
                fileSts = FileStatus.FileExists;

            // Debug
            log4j.debug("DBD#fileExists() nameWithStream=" + name + ", fileSts=" + FileStatus.asString(fileSts));
        } else {

            // Get, or create, the file state for the path

            fstate = getFileState(name, dbCtx, true);

            // Check if the file exists status has been cached

            fileSts = fstate.getFileStatus();

            // if ( fstate.getFileStatus() == FileStatus.Unknown) {
            String ext = DiskUtil.getExt(name);
            if (fstate.getFileStatus() == FileStatus.Unknown
                    || (fstate.getFileStatus() == FileStatus.NotExist && DBUtil.SUPPORT_EXT.contains(ext))) {
                // fstate.getFileStatus()==FileStatus.NotExist
                // &&DBUtil.SUPPORT_EXT.contains(ext) ?

                // Get the file details

                DBFileInfo dbInfo = getFileDetails(name, dbCtx, fstate, userName, shareName, userId);

                if (dbInfo != null) {
                    if (dbInfo.isDirectory() == true)
                        fileSts = FileStatus.DirectoryExists;
                    else
                        fileSts = FileStatus.FileExists;

                    // Save the file id

                    if (dbInfo.getFileId() != -1)
                        fstate.setFileId(dbInfo.getFileId());
                } else {

                    // Indicate that the file does not exist

                    fstate.setFileStatus(FileStatus.NotExist);
                    fileSts = FileStatus.NotExist;
                }

                // Debug
                // log4j.debug("DBD#fileExists() name=" + name + ", fileSts=" +
                // FileStatus.asString(fileSts));
            } else {

                // DEBUG
                // log4j.debug("@@ Cache hit - fileExists() name=" + name +
                // ", fileSts=" + FileStatus.asString(fileSts));
            }
        }

        // Return the file exists status

        return fileSts;
    }

    /**
     * Flush buffered data for the specified file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param file
     *            Network file
     * @exception IOException
     */
    public void flushFile(SrvSession sess, TreeConnection tree, NetworkFile file) throws IOException {

        // Debug
        log4j.debug("DBD#flushFile()");

        // Flush any buffered data

        file.flushFile();

    }

    /**
     * Return file information about the specified file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param name
     *            File name
     * @return SMBFileInfo
     * @exception IOException
     */
    public FileInfo getFileInformation(SrvSession sess, TreeConnection tree, String name) throws IOException {

        // Check for the null file name
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        if (name == null)
            return null;
        // Access the JDBC context
        String shareName = DiskUtil.findFristPath(name, tree.getFullName());

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Database is offline");
            throw new DiskOfflineException("Database is offline");
        }

        // Check if the path is a file stream

        FileState fstate = null;
        FileInfo finfo = null;

        if (FileName.containsStreamName(name)) {

            // Check if there is an active file state for the stream

            fstate = getFileState(name, dbCtx, true);
            // fstate = getFileState(name,dbCtx,false);//?

            if (fstate != null) {

                // Check if the file stream exists

                if (fstate.getFileStatus() != FileStatus.NotExist) {

                    // Check if the file information is available

                    finfo = (FileInfo) fstate.findAttribute(FileState.FileInformation);
                } else
                    return null;
            }

            // If the cached file information is not available then create it

            if (finfo == null) {

                // Get, or create, the file state for main file path

                String filePath = FileName.getParentPathForStream(name);
                FileState parent = getFileState(filePath, dbCtx, false);

                // Get the file information for the parent file to load the
                // cache

                if (parent == null) {

                    // Get the file information for the parent file
                    log4j.debug("parent is null ?:getFileInformation() ; filepath:" + filePath
                            + ", finfo:" + finfo);
                    getFileInformation(sess, tree, filePath);

                    // File state should exist for the parent now

                    // parent = getFileState(filePath,dbCtx,false);
                    parent = getFileState(filePath, dbCtx, true);// ?
                }

                // Check if the top level file exists

                if (parent != null && parent.fileExists() == true) {

                    // Get the top level file details

                    DBFileInfo dbInfo = getFileDetails(name, dbCtx, parent, userName, shareName, userId);

                    if (dbInfo != null) {

                        // Get the list of available streams

                        StreamInfoList streams = getStreamList(sess, tree, filePath);

                        if (streams != null && streams.numberOfStreams() > 0) {

                            // Parse the path into directory, file and stream
                            // names

                            String[] paths = FileName.splitPathStream(name);

                            // Get the details for the stream, if the
                            // information is valid copy it to a file
                            // information
                            // object

                            StreamInfo sInfo = streams.findStream(paths[2]);

                            if (sInfo != null) {

                                // Create a file information object, copy the
                                // stream details to it

                                finfo = new DBFileInfo(paths[1], name, dbInfo.getFileId(), dbInfo.getDirectoryId());

                                finfo.setFileId(sInfo.getFileId());
                                finfo.setFileSize(sInfo.getSize());

                                finfo.setCreationDateTime(sInfo.getCreationDateTime());
                                finfo.setAccessDateTime(sInfo.getAccessDateTime());
                                finfo.setModifyDateTime(sInfo.getModifyDateTime());

                                // Attach to the file state

                                fstate.addAttribute(FileState.FileInformation, finfo);

                                // DEBUG
                                log4j.debug("DBD#getFileInformation() stream=" + name + ", info=" + finfo);
                            }
                        }
                    }
                }
            }
        } else {

            // Get, or create, the file state for the path

            fstate = getFileState(name, dbCtx, true);
            // fstate = getFileState(name, dbCtx, false);

            // Get the file details for the path

            DBFileInfo dbInfo = getFileDetails(name, dbCtx, fstate, userName, shareName, userId);

            // Set the full file/path name

            if (dbInfo != null)
                dbInfo.setFullName(name);
            finfo = dbInfo;
        }
        // DEBUG

        // if ( Debug.EnableInfo && hasDebug() && finfo != null)
        // Debug.println("getFileInformation info=" + finfo.toString());

        // Return the file information

        //      if (finfo !=null && !finfo.isDirectory()) {
        //         // TODO
        //         boolean b = finfo.hasAttribute(FileAttribute.ReadOnly);
        //         if (!b) {
        //            DBFileInfo f = (DBFileInfo) finfo;
        //            boolean open = VirtuaServerSyncServlet.isFileOpen(userId, f.getFullName(), shareName);
        //            if (open) {
        //               int at = f.getFileAttributes();
        //               at += FileAttribute.ReadOnly;
        //               f.setFileAttributes(at);
        //            }
        //         }
        //      }

        return finfo;
    }

    /**
     * Determine if the disk device is read-only.
     * 
     * @param sess
     *            Session details
     * @param ctx
     *            Device context
     * @return true if the device is read-only, else false
     * @exception IOException
     *                If an error occurs.
     */
    public boolean isReadOnly(SrvSession sess, DeviceContext ctx) throws IOException {
        return false;
    }

    /**
     * Open a file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param params
     *            File open parameters
     * @return NetworkFile
     * @exception IOException
     */
    public NetworkFile openFile(SrvSession sess, TreeConnection tree, FileOpenParams params) throws IOException {

        // Access the JDBC context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Database is offline");
            throw new DiskOfflineException("Database is offline");
        }

        String shareName = DiskUtil.findFristPath(params.getPath(), tree.getFullName());

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

        // Get, or create, the file state
        FileState fstate = getFileState(params.getPath(), dbCtx, true);

        // Check if the file has a data update in progress, the file will be
        // offline until the
        // data update completes

        if (fstate != null && fstate.hasDataUpdateInProgress()) {
            log4j.error("DBD#Data update in progress");
            throw new FileOfflineException("Data update in progress");
        }

        // Check if we are opening a stream associated with the main file

        if (fstate != null && params.isStream()) {

            // Open an NTFS stream

            return openStream(sess, tree, params, fstate, dbCtx);
        }

        // Get the file name

        // DEBUG
        // log4j.debug("DBD#openFile FileName [1]= " + fname);

        // Check if the file name is too long

        if (fname != null && fname.length() > MaxFileNameLen) {
            log4j.error("DBD#File name too long, " + fname);
            throw new FileNameException("File name too long, " + fname);
        }

        // Get the file information

        DBFileInfo finfo = getFileDetails(params.getPath(), dbCtx, fstate, userName, shareName, userId);

        //      String name = finfo.getFileName();
        //      if (name.startsWith("nsh")&& name.endsWith(".docx")) {
        //         log4j.debug("DBDiskLDriver.oooo00000 " + name + " / " + fstate.getOpenCount());
        //      }

        if (finfo == null) {
            // throw new AccessDeniedException();
            log4j.error("DBD#openFile FileNotFoundException, fname:" + fname);
            throw new FileNotFoundException();
        }

        // If retention is enabled get the expiry date/time

        if (dbCtx.hasRetentionPeriod()) {
            try {

                // Get the file retention expiry date/time

                RetentionDetails retDetails = dbCtx.getDBInterface().getFileRetentionDetails(finfo.getDirectoryId(),
                        finfo.getFileId());
                if (retDetails != null)
                    fstate.setRetentionExpiryDateTime(retDetails.getEndTime());
            } catch (DBException ex) {
                log4j.error("DBD#Retention error, " + ex.getMessage());
                throw new AccessDeniedException("Retention error, " + ex.getMessage());
            }
        }

        // Check if the current file open allows the required shared access

        FileAccessToken accessToken = null;

        if (params.getPath().equals("\\") == false) {

            // Check if the file can be opened in the requested mode

            accessToken = dbCtx.getStateCache().grantFileAccess(params, fstate,
                    finfo.isDirectory() ? FileStatus.DirectoryExists : FileStatus.FileExists);
        }

        // DEBUG
        // log4j.debug("DBD#openFile() name=" + params.getPath() +
        // ", sharing=0x" + Integer.toHexString(params.getSharedAccess()) +
        // ", PID=" + params.getProcessId() + ", token=" + accessToken);

        DBNetworkFile jdbcFile = null;

        try {
            // Create a JDBC network file and open the top level file

            jdbcFile = (DBNetworkFile) dbCtx.getFileLoader().openFile(params, finfo.getFileId(), 0,
                    finfo.getDirectoryId(), false, finfo.isDirectory(), userName);

            jdbcFile.setFileDetails(finfo);
            jdbcFile.setFileState(dbCtx.getStateCache().getFileStateProxy(fstate));

            jdbcFile.openFile(false);

            // Set the granted file access

            if (params.isReadOnlyAccess())
                jdbcFile.setGrantedAccess(NetworkFile.READONLY);
            else if (params.isWriteOnlyAccess())
                jdbcFile.setGrantedAccess(NetworkFile.WRITEONLY);
            else
                jdbcFile.setGrantedAccess(NetworkFile.READWRITE);

            // Set the file owner

            if (sess != null)
                jdbcFile.setOwnerSessionId(sess.getUniqueId());

            // Save the access token

            jdbcFile.setAccessToken(accessToken);

            if (!params.isDirectory()) {// ??locked?locked??
                boolean b = finfo.isLocked();
                String ext = DiskUtil.getExt(fname);
                if (!b && ext != null && DBUtil.SUPPORT_EXT.contains(ext) && !ext.equals("txt")
                        && !fname.startsWith("~$")) {

                    if (!params.isAttributesOnlyAccess() && params.isReadOnlyAccess()) {
                        int createOptions = params.getCreateOptions();
                        int openCount = fstate.getOpenCount();

                        //ext.equals("xls") || 
                        if (ext.equals("doc")) {// wps wps doc // 2010 doc??,wpsok
                            //                  if (openCount == 2) {

                            if (createOptions == 64 && openCount == 3) {
                                MySQLDBLInterface f = (MySQLDBLInterface) dbCtx.getDBInterface();
                                f.lock(userId, finfo.getFileId(), shareName);
                            }
                        } else if (ext.equals("ppt")) {// wps wps ppt    && access == 3 ,? // 2010 xls???

                            if (createOptions == 64 && openCount == 2) {
                                MySQLDBLInterface f = (MySQLDBLInterface) dbCtx.getDBInterface();
                                f.lock(userId, finfo.getFileId(), shareName);
                            }

                        } else if (ext.equals("xls")) {// wps wps xls  // 2010 xls??,wpsok

                            if (createOptions == 64 && openCount == 2) {
                                MySQLDBLInterface f = (MySQLDBLInterface) dbCtx.getDBInterface();
                                f.lock(userId, finfo.getFileId(), shareName);
                            }
                        } else if (createOptions > 64 && openCount == 2) {

                            MySQLDBLInterface f = (MySQLDBLInterface) dbCtx.getDBInterface();
                            f.lock(userId, finfo.getFileId(), shareName);
                        }
                    }
                }
            }

        } finally {

            // If the file object is not valid then release the file access that
            // was granted

            if (jdbcFile == null)
                dbCtx.getStateCache().releaseFileAccess(fstate, accessToken);
        }

        // Return the network file

        return jdbcFile;
    }

    /**
     * Read a block of data from a file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param file
     *            Network file
     * @param buf
     *            Buffer to return data to
     * @param bufPos
     *            Starting position in the return buffer
     * @param siz
     *            Maximum size of data to return
     * @param pos
     *            File offset to read data
     * @return Number of bytes read
     * @exception IOException
     */
    public int readFile(SrvSession sess, TreeConnection tree, NetworkFile file, byte[] buf, int bufPos, int siz,
            long pos) throws IOException {

        // Debug
        // log4j.debug("DBD#readFile() fileId:"+file.getFileId()+",name:"+file.getName()+",fileSize:"+file.getFileSize()+",filePos="
        // + pos + ", len=" + siz);
        // Access the JDBC context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Database is offline");
            throw new DiskOfflineException("Database is offline");
        }

        // Check that the network file is our type

        int rxsiz = 0;

        if (file instanceof DBNetworkFile) {

            // Access the JDBC network file

            DBNetworkFile jfile = (DBNetworkFile) file;

            // Check if there are any locks on the file

            // Check if there are any locks on the file

            if (jfile.hasFileState() && jfile.getFileState().hasActiveLocks()) {

                // Check if this session has write access to the required
                // section of the file

                if (jfile.getFileState().canReadFile(pos, siz, sess.getProcessId()) == false) {
                    log4j.error("DBD#readFile() LockConflictException jfile.getFileState().canReadFile == false");
                    throw new LockConflictException();
                }
            }

            // Read from the file

            rxsiz = jfile.readFile(buf, siz, bufPos, pos);

            // Check if we have reached the end of file
            if (rxsiz == -1) {
                log4j.debug("DBD#readFile() rxsiz==-1 ?");
                rxsiz = 0;
            }
        }

        // Return the actual read length

        return rxsiz;
    }

    /**
     * Rename a file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param oldName
     *            Existing file name
     * @param newName
     *            New file name
     * @exception IOException
     */
    public void renameFile(SrvSession sess, TreeConnection tree, String oldName, String newName)
            throws IOException {

        // Debug
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        String ipAddress = sess.getClientInformation().getClientAddress();
        // String shareName = tree.getContext().getShareName();
        log4j.debug(
                "DBD#renameFile() ,time:" + System.currentTimeMillis() + ", from=" + oldName + " to=" + newName);
        // if (oldName.equalsIgnoreCase("\\"+userName+DBUtil.SPECIAL_CHAR)) {
        // log4j.warn(oldName+" ,????");
        // throw new AccessDeniedException("????");
        // }
        //
        // ????
        // (:username@/; username@/)
        //

        if (oldName.equalsIgnoreCase("\\" + DBUtil.SHARENAME_USERFILE)
                || oldName.equalsIgnoreCase("\\" + DBUtil.SHARENAME_RECIVEFILE)
                || oldName.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                || oldName.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE_ALIAS)) {
            log4j.warn(oldName + " ,????");
            throw new AccessDeniedException("????");
        }

        // Get the file name

        String[] paths = FileName.splitPath(newName);
        String newFname = paths[1];

        if (paths.length == 2) {
            String firstPath = paths[0];
            if (firstPath.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                    || firstPath.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE_ALIAS)) {
                throw new AccessDeniedException("?????");
            }
        }

        String[] oldPaths = FileName.splitPath(oldName);
        String oldFname = oldPaths[1];
        if (oldPaths.length == 2) {
            String firstPath = oldPaths[0];
            if (firstPath.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                    || firstPath.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE_ALIAS)) {
                throw new AccessDeniedException("?????");
            }
        }
        // Access the JDBC context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Data is offline");
            throw new DiskOfflineException("Database is offline");
        }
        String shareName = DiskUtil.findFristPath(oldName, tree.getFullName());

        // Get, or create, the file state for the existing file

        FileState fstate = getFileState(oldName, dbCtx, true);

        try {

            // Check if the file name is too long

            if (newFname != null && newFname.length() > MaxFileNameLen) {
                log4j.error("Destination name too long, " + newFname);
                throw new FileNameException("Destination name too long, " + newFname);
            }

            // Check if the file is within an active retention period

            getRetentionDetailsForState(dbCtx, fstate);

            if (fstate.hasActiveRetentionPeriod()) {
                log4j.error("File retention active");
                throw new AccessDeniedException("File retention active");
            }

            // Get the file id of the existing file

            int fid = fstate.getFileId();
            int dirId = -1;

            if (fid == -1) {

                // Split the current path string and find the file id of the
                // existing file/directory

                dirId = findParentDirectoryId(dbCtx, oldName, true, userName, shareName, userId);
                if (dirId == -1) {
                    log4j.error("DBD#renameFile() dirId==-1 FileNotFoundException ,name:" + oldName);
                    throw new FileNotFoundException(oldName);
                }

                // Get the current file/directory name

                // Get the file id

                fid = getFileId(oldName, oldFname, dirId, dbCtx, userId, shareName, userName);
                if (fid == -1) {
                    log4j.error("DBD#renameFile() fid==-1 FileNotFoundException ,name:" + oldName);
                    throw new FileNotFoundException(oldName);
                }
                // Update the file state

                fstate.setFileId(fid);
            }

            // Get the existing file/directory details

            DBFileInfo curInfo = getFileDetails(oldName, dbCtx, fstate, userName, shareName, userId);
            if (dirId == -1 && curInfo != null)
                dirId = curInfo.getDirectoryId();

            // Get the new file/directory name

            int newDirId = findParentDirectoryId(dbCtx, newName, true, userName, shareName, userId);
            if (newDirId == -1) {
                log4j.error("DBD#renameFile() newDirId==-1 FileNotFoundException , name:" + newName);
                throw new FileNotFoundException(newName);
            }

            // Rename the file/folder, this may also link the file/folder to
            // a new parent directory
            int newFid = 0;
            if (curInfo != null) {
                if (curInfo.isDirectory()) { // fid id
                    //int uid = sess.getClientInformation().getUid();
                    if (!newFname.equals(oldFname)) {// ???   
                        newFid = (int) dbCtx.getDBInterface().renameFolderRecord(newDirId, -1, fid,
                                curInfo.getUid(), newFname, shareName, ipAddress);

                    } else { //                   
                        newFid = (int) dbCtx.getDBInterface().renameFolderRecord(newDirId, dirId, fid,
                                curInfo.getUid(), newFname, shareName, ipAddress);
                    }
                } else {
                    if (!newFname.equals(oldFname)) {
                        newFid = dbCtx.getDBInterface().renameFileRecord(dirId, fid, newFname, newDirId, shareName,
                                ipAddress);
                    } else { // 
                        newFid = (int) dbCtx.getDBInterface().renameFolderRecord(newDirId, dirId, fid,
                                curInfo.getUid(), newFname, shareName, ipAddress);
                    }
                }
            }

            // ???
            FileSegmentInfo fileSegInfo = (FileSegmentInfo) fstate
                    .findAttribute(ObjectIdFileLoader.DBFileSegmentInfo);
            if (null != fileSegInfo) {

                // log4j.fatal("0000 DBD#renameFile() modifyFileTemporaryFilea  AAAA !! ");
                // xls:  Microsoft Excel .xls ??--?xls??? ???
                //            if (oldFname.endsWith(".xls") && newFname.endsWith(".xls")) {
                //               
                //            } else
                //               dbCtx.getDBInterface().modifyFileTemporaryFile(newFid,
                //                  fileSegInfo, shareName);// null
            }
            if (newFid != fid) {
                log4j.debug("gonna to reset the fid in the state ,this is very important !!!! ");

                if (dbCtx.getFileLoader() instanceof NamedFileLoader) {
                    // Rename the file/directory
                    NamedFileLoader namedLoader = (NamedFileLoader) dbCtx.getFileLoader();
                    namedLoader.renameFileDirectory(oldName, newFid, newName, curInfo.isDirectory());
                }
                fstate = getFileState(newName, dbCtx, true);
                // file id??
                if (null != fstate) {
                    fstate.setFileId(newFid);
                    if (null != fileSegInfo) {
                        File temFile = new File(fileSegInfo.getTemporaryFile());
                        if (null != temFile && temFile.length() > 0) {
                            fstate.setFileSize(temFile.length());// ??
                        }
                    }
                }
                fstate.setFileStatus(FileStatus.FileExists, FileState.ReasonFileCreated);

                String ext = DiskUtil.getExt(newName);
                if (DBUtil.SUPPORT_EXT.contains(ext)) {
                    VirtuaServerSyncServlet.notifyOthers(userId, newDirId, newFid, newName, newFname, shareName);
                }

                //            log4j.debug("DBDiskLDriver.renameFile()");
                //            fstate.setExpiryTime(0);
                /*
                 * ????????? //? fstate.setFileStatus(
                 * FileStatus.NotExist, FileState.ReasonFileDeleted);
                 * fstate.setFileId(-1);
                 * fstate.removeAttribute(FileState.FileInformation); fstate
                 * = getFileState(newFname, dbCtx, true);//? //
                 * fstate.setFileStatus( FileStatus.FileExists,
                 * FileState.ReasonFileCreated); // Save the file id
                 * fstate.setFileId(newFid);
                 */
            } else {
                if (dbCtx.getFileLoader() instanceof NamedFileLoader) {
                    // Rename the file/directory
                    NamedFileLoader namedLoader = (NamedFileLoader) dbCtx.getFileLoader();
                    namedLoader.renameFileDirectory(oldName, fid, newName, curInfo.isDirectory());
                }

            }
            // Update the file state with the new file name/path

            /**
             * 
             * 
             * TODO needed ?
             */
            dbCtx.getStateCache().renameFileState(newName, fstate, curInfo.isDirectory());
            fstate.removeAllAttributes();

        } catch (DBException ex) {
            log4j.error("DBD#renameFile() FileNotFoundException , name:" + oldName + " , newName:" + newName, ex);
            throw new FileNotFoundException(oldName);
        }
    }

    /**
     * Seek to the specified point within a file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param file
     *            Network file
     * @param pos
     *            New file position
     * @param typ
     *            Seek type
     * @return New file position
     * @exception IOException
     */
    public long seekFile(SrvSession sess, TreeConnection tree, NetworkFile file, long pos, int typ)
            throws IOException {

        // Debug
        log4j.error("DBD#seekFile() pos:" + pos + " , typ:" + typ);

        // Check that the network file is our type

        long newpos = 0;

        if (file instanceof DBNetworkFile) {

            // Seek within the file

            DBNetworkFile jfile = (DBNetworkFile) file;
            newpos = jfile.seekFile(pos, typ);
        }

        // Return the new file position

        return newpos;
    }

    /**
     * Set file information
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param name
     *            File name
     * @param info
     *            File information to be set
     * @exception IOException
     */
    public void setFileInformation(SrvSession sess, TreeConnection tree, String name, FileInfo info)
            throws IOException {

        // Debug
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        log4j.debug("DBD#setFileInformation() name=" + name + ", info=" + info + ", set flags="
                + info.getSetFileInformationFlagsString());

        // If the only flag set is the delete on close flag then return, nothing
        // to do

        if (info.getSetFileInformationFlags() == FileInfo.SetDeleteOnClose)
            return;

        // Access the JDBC context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Database is offline");
            throw new DiskOfflineException("Database is offline");
        }

        // Get, or create, the file state

        FileState fstate = getFileState(name, dbCtx, true);

        // Get the file details
        String shareName = DiskUtil.findFristPath(name, tree.getFullName());

        DBFileInfo dbInfo = getFileDetails(name, dbCtx, fstate, userName, shareName, userId);
        if (dbInfo == null) {
            log4j.error("FileNotFoundException :" + name);
            throw new FileNotFoundException(name);
        }

        try {

            // Check if the file is within an active retention period

            getRetentionDetailsForState(dbCtx, fstate);

            if (fstate.hasActiveRetentionPeriod())
                throw new AccessDeniedException("File retention active");

            // Check if the loader handles set file information requests, an
            // exception may be thrown by the loader
            // to prevent the update

            if (dbCtx.getFileLoader() instanceof NamedFileLoader) {

                // Set the file information

                NamedFileLoader namedLoader = (NamedFileLoader) dbCtx.getFileLoader();
                namedLoader.setFileInformation(name, dbInfo.getFileId(), info);
            }

            // Validate any timestamp updates
            //
            // Switch off invalid updates from being written to the database but
            // allow them to be cached.
            // To allow test apps such as IFSTEST to complete successfully.

            int origFlags = info.getSetFileInformationFlags();
            int dbFlags = origFlags;

            if (info.hasSetFlag(FileInfo.SetAccessDate) && info.getAccessDateTime() > MaxTimestampValue)
                dbFlags -= FileInfo.SetAccessDate;

            if (info.hasSetFlag(FileInfo.SetCreationDate) && info.getCreationDateTime() > MaxTimestampValue)
                dbFlags -= FileInfo.SetCreationDate;

            if (info.hasSetFlag(FileInfo.SetModifyDate) && info.getModifyDateTime() > MaxTimestampValue)
                dbFlags -= FileInfo.SetModifyDate;

            // Check if the inode change date/time has been set

            if (info.hasChangeDateTime() == false) {
                info.setChangeDateTime(System.currentTimeMillis());
                if (info.hasSetFlag(FileInfo.SetChangeDate) == false)
                    info.setFileInformationFlags(info.getSetFileInformationFlags() + FileInfo.SetChangeDate);
            } else if (info.hasSetFlag(FileInfo.SetChangeDate) && info.getChangeDateTime() > MaxTimestampValue)
                dbFlags -= FileInfo.SetChangeDate;

            // Check if file attributes are being set

            if (info.hasSetFlag(FileInfo.SetAttributes)) {

                // Check if this is a folder, make sure the Directory attribute
                // does not get reset

                if (dbInfo.isDirectory() && (info.getFileAttributes() & FileAttribute.Directory) == 0)
                    info.setFileAttributes(info.getFileAttributes() + FileAttribute.Directory);
            }

            // Update the information flags for the database update

            info.setFileInformationFlags(dbFlags);

            // Update the file information

            if (dbFlags != 0)
                dbCtx.getDBInterface().setFileInformation(dbInfo.getDirectoryId(), dbInfo.getFileId(), info,
                        shareName);

            // Use the original information flags when updating the cached file
            // information details

            info.setFileInformationFlags(origFlags);

            // Copy the updated values to the file state

            if (info.hasSetFlag(FileInfo.SetFileSize))
                dbInfo.setFileSize(info.getSize());

            if (info.hasSetFlag(FileInfo.SetAllocationSize))
                dbInfo.setAllocationSize(info.getAllocationSize());

            if (info.hasSetFlag(FileInfo.SetAccessDate))
                dbInfo.setAccessDateTime(info.getAccessDateTime());

            if (info.hasSetFlag(FileInfo.SetCreationDate))
                dbInfo.setAccessDateTime(info.getCreationDateTime());

            if (info.hasSetFlag(FileInfo.SetModifyDate))
                dbInfo.setAccessDateTime(info.getModifyDateTime());

            if (info.hasSetFlag(FileInfo.SetChangeDate))
                dbInfo.setAccessDateTime(info.getChangeDateTime());

            if (info.hasSetFlag(FileInfo.SetGid))
                dbInfo.setGid(info.getGid());

            if (info.hasSetFlag(FileInfo.SetUid))
                dbInfo.setUid(info.getUid());

            if (info.hasSetFlag(FileInfo.SetMode))
                dbInfo.setMode(info.getMode());

            if (info.hasSetFlag(FileInfo.SetAttributes))
                dbInfo.setFileAttributes(info.getFileAttributes());

            // Update the file state

            fstate.setFileId(dbInfo.getFileId());
        } catch (DBException ex) {
            log4j.error(ex);
            throw new IOException();
        }
    }

    /**
     * Start a search of the file system
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param searchPath
     *            String
     * @param attrib
     *            int
     * @return SearchContext
     * @exception FileNotFoundException
     */
    public SearchContext startSearch(SrvSession sess, TreeConnection tree, String searchPath, int attrib)
            throws FileNotFoundException {

        // Access the JDBC context
        // String shareName = tree.getContext().getShareName();
        String shareName = DiskUtil.findFristPath(searchPath);
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // log4j.debug("DBD#startSearch  searchPath:"+searchPath+" , userName:"+userName+" , dbCtx:"+dbCtx);
        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false) {
            log4j.error("Database is offline");
            throw new FileNotFoundException("Database is offline");
        }
        // debug
        // log4j.debug("DBD#startSearch searchPath:"+searchPath+", shareName:"+tree.getContext().getShareName()+" ,attrib:"+attrib);

        // Prepend a leading slash to the path if not on the search path
        if (searchPath.startsWith("\\") == false)
            searchPath = "\\" + searchPath;
        // if(StringUtils.isNotEmpty(searchPath) && !searchPath.equals("\\") &&
        // !searchPath.equals("\\*") &&
        // searchPath.toLowerCase().startsWith("\\"+userName.toLowerCase()+DBUtil.SPECIAL_CHAR)==false)
        // {
        // log4j.warn("DBD#startSearch  ??searchPath!  searchPath:"+searchPath);
        // return null;
        // }

        // Get the directory id for the last directory in the path
        // log4j.debug("DBD#startSearch findParentDirectoryId  ,searchPath:"+searchPath);
        int dirId = findParentDirectoryId(dbCtx, searchPath, true, userName, shareName, userId);
        if (dirId == -1) {
            // log4j.error("DBD#startSearch dirId==-1, Invalid Path "+searchPath);
            throw new FileNotFoundException("Invalid path");
        }

        // Start the search

        SearchContext search = null;

        try {

            // Check if the search path is a none wildcard search, the file
            // information may be in the
            // state cache

            if (WildCard.containsWildcards(searchPath) == false) {

                // Check if there is a file state for the search path

                FileState searchState = getFileState(searchPath, dbCtx, false);
                if (searchState != null && searchState.fileExists() == true) {

                    // Check if the file state has the file information attached

                    DBFileInfo finfo = (DBFileInfo) searchState.findAttribute(FileState.FileInformation);

                    if (finfo != null) {

                        // Create a single file search context using the cached
                        // file information
                        // log4j.debug("DBD#startSearch finfo != null  ,finfo:"+finfo.getFileId()+" ,fullName:"+finfo.getFullName());
                        search = new CachedSearchContext(finfo);

                        // DEBUG
                        // log4j.debug("DBD#StartSearch using cached file information, path="
                        // + searchPath + ", info=" + finfo);
                    }
                }
            }

            // Start the search via the database interface, if the search is not
            // valid

            if (search == null) {

                if (dirId <= 0 && searchPath.indexOf("\\", 2) == -1
                        && tree.getContext().getShareName().indexOf(DBUtil.SPECIAL_CHAR) > 0) {
                    // cache?
                    CacheManager cacheManager = CacheManagerUtil.getCacheManager();
                    Cache cache = null;

                    if (null != cacheManager) {
                        cache = cacheManager.getCache("SearchSharesCache");
                        Element element = cache.get(searchPath);
                        if (null != element && element.getObjectValue() != null) {
                            search = (SearchContext) element.getObjectValue();
                            // if(null != context &&
                            // context.numberOfEntries()>0)
                            if (null != search && search.getMaximumFiles() > 0) {
                                log4j.debug("@@ Cache hit - SearchSharesCache :" + searchPath);
                                return search;
                            }
                        }
                    }

                    // Start the search

                    DBSearchContext dbSearch = dbCtx.getDBInterface().startSearch(dirId, searchPath, attrib,
                            DBInterface.FileAll, -1, userName, shareName);

                    // Check if files should be marked as offline

                    dbSearch.setMarkAsOffline(dbCtx.hasOfflineFiles());
                    dbSearch.setOfflineFileSize(dbCtx.getOfflineFileSize());

                    // log4j.debug("DBD#startSearch getDBInterface().startSearch end ,dirId:"+dirId+" , searchPath:"+searchPath);
                    search = dbSearch;

                    if (null != search && search.getMaximumFiles() > 0) {
                        // 
                        Element element = new Element(searchPath, search);
                        if (null != cache) {
                            cache.put(element);
                        }
                    }

                    return search;
                }

                // Start the search

                DBSearchContext dbSearch = dbCtx.getDBInterface().startSearch(dirId, searchPath, attrib,
                        DBInterface.FileAll, -1, userName, shareName);

                // Check if files should be marked as offline

                dbSearch.setMarkAsOffline(dbCtx.hasOfflineFiles());
                dbSearch.setOfflineFileSize(dbCtx.getOfflineFileSize());

                // log4j.debug("DBD#startSearch getDBInterface().startSearch end ,dirId:"+dirId+" , searchPath:"+searchPath);
                search = dbSearch;
            }
        } catch (DBException ex) {
            log4j.error(ex);
            throw new FileNotFoundException();
        }

        // Return the search context

        return search;
    }

    /**
     * Truncate a file to the specified size
     * 
     * @param sess
     *            Server session
     * @param tree
     *            Tree connection
     * @param file
     *            Network file details
     * @param siz
     *            New file length
     * @exception java.io.IOException
     *                The exception description.
     */
    public void truncateFile(SrvSession sess, TreeConnection tree, NetworkFile file, long siz)
            throws java.io.IOException {

        // Debug
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();

        // Check that the network file is our type

        if (file instanceof DBNetworkFile) {

            // Access the JDBC context

            DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

            // Get the JDBC file

            DBNetworkFile jfile = (DBNetworkFile) file;

            // log4j.debug("DBD#truncateFile(),fullName:"+jfile.getFullName());
            if (jfile.isStream()) {
                log4j.warn("DBD#truncateFile(),?? fullName:" + jfile.getFullName());
                return;
            }

            // Get, or create, the file state

            FileState fstate = jfile.getFileState();

            String shareName = DiskUtil.findFristPath(jfile.getFullName());

            // Get the file details

            DBFileInfo dbInfo = getFileDetails(jfile.getFullName(), dbCtx, fstate, userName, shareName, userId);
            if (dbInfo == null) {
                log4j.error("FileNotFoundException ,fullName:" + jfile.getFullName());
                throw new FileNotFoundException(jfile.getFullName());
            }

            // Check if the new file size is greater than the maximum allowed
            // file size, if enabled

            if (dbCtx.hasMaximumFileSize() && siz > dbCtx.getMaximumFileSize()) {

                // Mark the file to delete on close

                file.setDeleteOnClose(true);

                // Return a disk full error
                log4j.error("Write is beyond maximum allowed file size");
                throw new DiskFullException("Write is beyond maximum allowed file size");
            }

            // Keep track of the allocation/release size in case the file resize
            // fails

            long allocSize = 0L;
            long releaseSize = 0L;

            // Check if there is a quota manager

            QuotaManager quotaMgr = dbCtx.getQuotaManager();

            if (dbCtx.hasQuotaManager()) {

                // Determine if the new file size will release space or require
                // space allocating

                if (siz > dbInfo.getAllocationSize()) {

                    // Calculate the space to be allocated

                    allocSize = siz - dbInfo.getAllocationSize();

                    // Allocate space to extend the file

                    quotaMgr.allocateSpace(sess, tree, file, allocSize);
                } else {

                    // Calculate the space to be released as the file is to be
                    // truncated, release the space if
                    // the file truncation is successful

                    releaseSize = dbInfo.getAllocationSize() - siz;
                }
            }

            // Set the file length

            try {
                jfile.truncateFile(siz);
            } catch (IOException ex) {

                // Check if we allocated space to the file

                if (allocSize > 0 && quotaMgr != null)
                    quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, allocSize);

                // Rethrow the exception
                log4j.error(ex);
                throw ex;
            }

            // Check if space has been released by the file resizing

            if (releaseSize > 0 && quotaMgr != null)
                quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, releaseSize);

            // Update the file information

            if (allocSize > 0)
                dbInfo.setAllocationSize(dbInfo.getAllocationSize() + allocSize);
            else if (releaseSize > 0)
                dbInfo.setAllocationSize(dbInfo.getAllocationSize() - releaseSize);

            // Update the last file change date/time

            try {

                // Build the file information to set the change date/time

                FileInfo finfo = new FileInfo();

                finfo.setChangeDateTime(System.currentTimeMillis());
                finfo.setFileInformationFlags(FileInfo.SetChangeDate);

                // Set the file change date/time

                dbCtx.getDBInterface().setFileInformation(jfile.getDirectoryId(), jfile.getFileId(), finfo,
                        shareName);

                // Update the cached file information

                dbInfo.setChangeDateTime(finfo.getChangeDateTime());
                dbInfo.setAllocationSize(siz);
            } catch (Exception ex) {
                log4j.error(ex);
            }
        }
    }

    /**
     * Write a block of data to a file
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param file
     *            Network file
     * @param buf
     *            Data to be written
     * @param bufoff
     *            Offset of data within the buffer
     * @param siz
     *            Number of bytes to be written
     * @param fileoff
     *            Offset within the file to start writing the data
     */
    public int writeFile(SrvSession sess, TreeConnection tree, NetworkFile file, byte[] buf, int bufoff, int siz,
            long fileoff) throws IOException {

        // Debug
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        // log4j.debug("DBD#writeFile() fid:"+file.getFileId()+",size:"+file.getFileSize()+",isStream:"+file.isStream()+",name:"+file.getFullName()+" , siz:"+siz+", fileoff:"+fileoff);

        // Access the JDBC context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Check if the database is online

        if (dbCtx.getDBInterface().isOnline() == false)
            throw new DiskOfflineException("Database is offline");

        // Check if the file name is a stream

        if (file.isStream()) {
            log4j.warn("DBD#writeFile() NTFS Alternate Data Streams ??? ???? , fid:"
                    + file.getFileId() + ",name:" + file.getFullName() + " , siz:" + siz + ", fileoff:" + fileoff);
            // return 0;//
            return siz;
        }

        // Check that the network file is our type

        if (file instanceof DBNetworkFile) {

            // Access the JDBC network file

            DBNetworkFile jfile = (DBNetworkFile) file;

            // Check if there are any locks on the file

            if (jfile.hasFileState() && jfile.getFileState().hasActiveLocks()) {

                // Check if this session has write access to the required
                // section of the file

                if (jfile.getFileState().canWriteFile(fileoff, siz, sess.getProcessId()) == false)
                    throw new LockConflictException();
            }

            // Check if there is a maximum file size, if so then check if the
            // write is beyond the allowed file size

            if (dbCtx.hasMaximumFileSize() && (fileoff + siz) > dbCtx.getMaximumFileSize()) {

                // Mark the file to delete on close

                file.setDeleteOnClose(true);

                // Return a disk full error
                log4j.error("Write is beyond maximum allowed file size");
                throw new DiskFullException("Write is beyond maximum allowed file size");
            }

            // Check if there is a quota manager

            QuotaManager quotaMgr = dbCtx.getQuotaManager();

            if (quotaMgr != null) {

                // Get the file information
                String shareName = DiskUtil.findFristPath(jfile.getFullName());

                DBFileInfo finfo = getFileDetails(jfile.getFullName(), dbCtx, jfile.getFileState(), userName,
                        shareName, userId);
                if (finfo == null) {
                    log4j.error("FileNotFoundException name:" + jfile.getFullName());
                    throw new FileNotFoundException(jfile.getFullName());
                }

                // Check if the file requires extending

                long extendSize = 0L;
                long endOfWrite = fileoff + siz;

                if (endOfWrite > finfo.getSize()) {

                    // Calculate the amount the file must be extended

                    extendSize = endOfWrite - finfo.getSize();

                    // Allocate space for the file extend

                    quotaMgr.allocateSpace(sess, tree, file, extendSize);
                }

                // Write to the file

                try {
                    jfile.writeFile(buf, siz, bufoff, fileoff);
                } catch (IOException ex) {

                    // Check if we allocated space to the file

                    if (extendSize > 0 && quotaMgr != null)
                        quotaMgr.releaseSpace(sess, tree, file.getFileId(), null, extendSize);

                    // Rethrow the exception
                    log4j.error(ex);
                    throw ex;
                }

                // Update the file information

                if (extendSize > 0)
                    finfo.setAllocationSize(MemorySize.roundupLongSize(endOfWrite));
            } else {

                // Write to the file

                jfile.writeFile(buf, siz, bufoff, fileoff);
            }
        }

        // Return the actual write length

        return siz;
    }

    /**
     * Parse/validate the parameter string and create a device context for this
     * share
     * 
     * @param shareName
     *            String
     * @param args
     *            ConfigElement
     * @return DeviceContext
     * @exception DeviceContextException
     */
    public DeviceContext createContext(String shareName, ConfigElement args) throws DeviceContextException {

        // Check the arguments

        if (args.getChildCount() < 3) {
            log4j.error("Not enough context arguments");
            throw new DeviceContextException("Not enough context arguments");
        }

        // Check for the debug enable flags

        if (args.getChild("Debug") != null)
            m_debug = true;

        // Create the database device context

        DBDeviceContext ctx = new DBDeviceContext(args);
        // Return the database device context

        return ctx;
    }

    /**
     * Get the file id for a file
     * 
     * @param path
     *            String
     * @param dbCtx
     *            DBDeviceContext
     * @return DBFileInfo
     */
    protected final DBFileInfo getFileDetails(int userId, String path, DBDeviceContext dbCtx) {

        return getFileDetails(path, dbCtx, null, null, dbCtx.getShareName(), userId);
    }

    // private void ss(FileState fstate) {
    // // TODO Auto-generated method stub
    //
    // FileSegmentInfo segInfo = (FileSegmentInfo) fstate
    // .findAttribute("DBFileSegmentInfo");
    //
    // if (segInfo != null && segInfo.isQueued() == false
    // && segInfo.hasStatus() != FileSegmentInfo.SaveWait) {
    //
    // try {
    //
    // // Delete the temporary file
    //
    // segInfo.deleteTemporaryFile();
    //
    // // Debug
    // log4j.debug("$$ Deleted temporary file "
    // + segInfo.getTemporaryFile() + " [CLOSED] $$");
    // } catch (IOException ex) {
    // }
    // }
    //
    // }

    /**
     * Get the file id for a file
     * 
     * @param path
     *            String
     * @param dbCtx
     *            DBDeviceContext
     * @param fstate
     *            FileState
     * @return DBFileInfo
     */
    protected final DBFileInfo getFileDetails(String path, DBDeviceContext dbCtx, FileState fstate, String userName,
            String shareName, int userId) {
        if (path.endsWith(".dll") || path.endsWith(".wbk")) {

        }
        String ext = DiskUtil.getExt(path);
        if (fstate != null) {
            // Return the file information
            DBFileInfo finfo = (DBFileInfo) fstate.findAttribute(FileState.FileInformation);
            if (finfo != null) {
                // return finfo; //?
                // ?(finfo?)
                // ?
                java.util.Date now = new java.util.Date();
                return finfo;// 10()finfo
                // ?????
            }
        }

        // Check for the root directory
        if (path.length() == 0 || path.compareTo("\\") == 0) {
            // Get the root directory information from the device context
            DBFileInfo rootDir = dbCtx.getRootDirectoryInfo();

            // Mark the directory as existing
            if (fstate != null)
                fstate.setFileStatus(FileStatus.DirectoryExists);

            // log4j.debug("DBD#getFileDetails root   fileId:"+rootDir.getFileId()+" ,fileName:"
            // + rootDir.getFileName());

            return rootDir;
        }

        if (path.equalsIgnoreCase("\\" + userName + DBUtil.SPECIAL_CHAR)) // 
        {
            // Get the root directory information from the device context
            DBFileInfo rootDir = dbCtx.getRootDirectoryInfo();
            rootDir.setPath(path);
            rootDir.setFileName(path);
            rootDir.setFileName(userName + DBUtil.SPECIAL_CHAR);
            // Mark the directory as existing
            if (fstate != null)
                fstate.setFileStatus(FileStatus.DirectoryExists);

            // log4j.debug("DBD#getFileDetails root   fileId:"+rootDir.getFileId()+" , userName:"+userName+" ,fileName:"
            // + rootDir.getFileName());

            return rootDir;
        }
        // ????
        // (:username@/; username@/)
        //
        if (path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_USERFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_RECIVEFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE_ALIAS)) {
            DBFileInfo rootDir = dbCtx.getRootDirectoryInfo();
            rootDir.setFileId(0);
            rootDir.setPath(path);
            rootDir.setFileName(path);
            rootDir.setFileName(userName + DBUtil.SPECIAL_CHAR);
            // Mark the directory as existing
            if (fstate != null)
                fstate.setFileStatus(FileStatus.DirectoryExists);
            // log4j.debug("DBD#getFileDetails root   fileId:"+rootDir.getFileId()+" , userName:"+userName+" ,fileName:"
            // + rootDir.getFileName());
            return rootDir;
        }

        // Split the path string and find the parent directory id
        int dirId = findParentDirectoryId(dbCtx, path, true, userName, shareName, userId);
        if (dirId == -1) {
            // log4j.warn("DBD#getFileDetails  findParentDirectoryId dirId =-1 ;path:"+path);
            return null;
        }

        // Strip any trailing slash from the path

        if (path.length() > 1 && path.endsWith(FileName.DOS_SEPERATOR_STR))
            path = path.substring(0, path.length() - 1);
        // log4j.debug("DBD#getFileDetails path.substring  path:"+path);
        // Get the file name

        String[] paths = FileName.splitPathStream(path);
        String fname = paths[1];

        String filePath = null;

        if (paths[0] != null && paths[0].endsWith(FileName.DOS_SEPERATOR_STR) == false)
            filePath = paths[0] + FileName.DOS_SEPERATOR_STR + paths[1];
        else
            filePath = paths[0] + paths[1];

        // Get the file id for the specified file
        // log4j.debug("DBD#getFileDetails path.substring path:"+path+" ,filePath:"+filePath);
        int fid = getFileId(filePath, fname, dirId, dbCtx, userId, shareName, userName);
        if (fid == -1) {
            // Set the file status as not existing
            if (fstate != null)
                fstate.setFileStatus(FileStatus.NotExist);

            return null;
        }

        // Create the file information

        DBFileInfo finfo = getFileInfo(filePath, dirId, fid, dbCtx, userName, shareName, userId);

        if (finfo != null && fstate != null) {

            // Set various file state details

            fstate.setFileStatus(finfo.isDirectory() ? FileStatus.DirectoryExists : FileStatus.FileExists);
            fstate.setFileId(finfo.getFileId());

            // Set the file name

            finfo.setFileName(fname);
            finfo.setFullName(path);

            // Check if files should be marked as offline

            if (dbCtx.hasOfflineFiles() && finfo.hasAttribute(FileAttribute.NTOffline) == false) {
                if (dbCtx.getOfflineFileSize() == 0 || finfo.getSize() >= dbCtx.getOfflineFileSize())
                    finfo.setFileAttributes(finfo.getFileAttributes() + FileAttribute.NTOffline);
            }
        } else if (finfo == null && fstate != null) {

            // Set the file status

            fstate.setFileStatus(FileStatus.NotExist);
        }

        // Check if the path is a file stream

        if (paths[2] != null) {
            // log4j.debug("DBD#getFileDetails paths[2] != null paths:"+paths);
            // Get the file information for the stream

            finfo = getStreamInfo(fstate, paths, dbCtx);
        }

        // Return the file/stream information

        return finfo;
    }

    /**
     * Get the file id for a file
     * 
     * @param path
     *            String
     * @param name
     *            String
     * @param dirId
     *            int
     * @param dbCtx
     *            DBDeviceContext
     * @return int
     */
    protected final int getFileId(String path, String name, int dirId, DBDeviceContext dbCtx, int userId,
            String shareName, String userName) {

        if (path.equalsIgnoreCase("\\" + userName + DBUtil.SPECIAL_CHAR) || path.equalsIgnoreCase("\\" + userName)) // 
        {
            return 0;// \admin@\
        }
        // ????
        // (:username@/; username@/)
        //
        if (path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_USERFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_RECIVEFILE)) // 
        {
            return 0;// \admin@\
        }
        // Check if the file is in the cache

        FileStateCache cache = dbCtx.getStateCache();
        FileState state = null;

        if (cache != null) {

            // Search for the file state

            state = cache.findFileState(path);
            if (state != null) {

                // Checkif the file id is cached
                if (state.getFileId() == -1) {
                    if (state.isDirectory())
                        return state.getFileId();// ??

                    // log4j.debug("@@ Cache hit - getFileId()==-1 name=" +
                    // name);
                    if (name.equalsIgnoreCase("desktop.ini") || name.equalsIgnoreCase("Thumbs.db")
                            || name.equalsIgnoreCase("folder.jpg") || name.equalsIgnoreCase("folder.gif")
                            || name.equalsIgnoreCase(".LFS"))
                        return state.getFileId();

                    //               log4j.debug("@@ Cache hit - getFileId()==-1 ,status:"
                    //                     + state.getFileStatus() + " ,path:" + path
                    //                     + ", name:" + name + " , dirId:" + dirId
                    //                     + " , shareName:" + shareName);
                }

                if (state.getFileId() != -1) {

                    // Debug
                    //log4j.debug("@@ Cache hit - getFileId() name=" + name);

                    // Return the file id

                    return state.getFileId();
                } else if (state.getFileStatus() == FileStatus.NotExist) {

                    // DEBUG
                    //log4j.debug("@@ Cache hit - getFileStatus() name=" + name
                    //      + ", sts=NotExist");

                    // Indicate that the file does not exist

                    if (state.getFileId() > 0) {
                        return state.getFileId();// ID?(?
                    }
                    return -1;
                }
            }
        }

        // Get the file id from the database

        int fileId = -1;

        try {

            // Get the file id

            //         log4j.debug();
            //         log4j.debug();
            //         log4j.debug("DBDiskLDriver.getFileId()");
            //         log4j.debug();
            //         log4j.debug();

            fileId = dbCtx.getDBInterface().getFileId(dirId, name, false, false, userId, shareName, userName);
        } catch (DBException ex) {
        }

        // Update the cache entry, if available

        if (state != null)
            state.setFileId(fileId);

        // Return the file id, or -1 if the file was not found

        return fileId;
    }

    /**
     * Load the retention details for a file state, if enabled
     * 
     * @param dbCtx
     *            DBDeviceContext
     * @param fstate
     *            FileState
     * @exception DBException
     */
    protected final void getRetentionDetailsForState(DBDeviceContext dbCtx, FileState fstate) throws DBException {

        // If retention is enabled get the expiry date/time

        if (dbCtx.hasRetentionPeriod()) {

            // Get the file retention expiry date/time

            RetentionDetails retDetails = dbCtx.getDBInterface().getFileRetentionDetails(-1, fstate.getFileId());
            if (retDetails != null)
                fstate.setRetentionExpiryDateTime(retDetails.getEndTime());
        }
    }

    /**
     * Find the directory id for the parent directory in the specified path
     * 
     * @param ctx
     *            DBDeviceContext
     * @param path
     *            String
     * @param filePath
     *            boolean
     * @return int
     */
    protected final int findParentDirectoryId(DBDeviceContext ctx, String path, boolean filePath, String userName,
            String shareName, int userId) {

        if (null != path && path.equalsIgnoreCase("\\" + userName + DBUtil.SPECIAL_CHAR))// 
            return 0;
        // ????
        // (:username@/; username@/)
        //
        if (path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_USERFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_RECIVEFILE)) // 
        {
            return 0;// \admin@\
        }

        // Split the path
        String[] paths = null;

        if (path != null && path.startsWith("\\")) {

            // Split the path
            paths = FileName.splitPath(path);
        } else {

            // Add a leading slash to the path before parsing

            paths = FileName.splitPath("\\" + path);
        }

        if (paths[0] != null && paths[0].compareTo("\\") == 0 || paths[0].startsWith("\\") == false)
            return 0;

        // Check if the file is in the cache

        FileStateCache cache = ctx.getStateCache();
        FileState state = null;

        if (cache != null) {

            // Search for the file state

            state = cache.findFileState(paths[0]);
            if (state != null && state.getFileId() != -1) {

                // Debug
                // int pid = 0;
                // String path1 = paths[0];

                // log4j.debug("@@ Cache hit - findParentDirectoryId() path=" +
                // paths[0]);

                // Return the file id

                return state.getFileId();
                // return pid;
            }
        }

        // Get the directory id list

        int[] ids = findParentDirectoryIdList(ctx, path, filePath, userName, shareName, userId);
        if (ids == null) {
            // log4j.warn("DBD#getFileDetails  findParentDirectoryIdList ids=null ;path:"+path);
            return -1;
        }

        // Check for the root directory id only

        if (ids.length == 1)
            return ids[0];

        // Return the directory id of the last directory

        int idx = ids.length - 1;
        if (filePath == true && ids[ids.length - 1] == -1)
            idx--;

        return ids[idx];
    }

    /**
     * Find the directory ids for the specified path list
     * 
     * @param ctx
     *            DBDeviceContext
     * @param path
     *            String
     * @param filePath
     *            boolean
     * @return int[]
     */
    protected final int[] findParentDirectoryIdList(DBDeviceContext ctx, String path, boolean filePath,
            String userName, String shareName, int userId) {
        // log4j.debug("DBD#findParentDirectoryIdList() path:"
        // +path+" , filePath:"+filePath+" ,userName:"+userName);
        // Validate the paths and check for the root path
        String[] paths = FileName.splitAllPaths(path);

        if (paths == null || paths.length == 0)
            return null;
        if (paths[0].compareTo("*.*") == 0 || paths[0].compareTo("*") == 0
                || (filePath == true && paths.length == 1) || path.equals("\\" + userName + DBUtil.SPECIAL_CHAR)) {
            int[] ids = { 0 };
            return ids;
        }
        // ????
        // (:username@/; username@/)
        //
        if (path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_USERFILE)
                || path.equalsIgnoreCase("\\" + DBUtil.SHARENAME_RECIVEFILE)) // 
        {
            int[] ids = { 0 };
            return ids;
        }
        if (paths[0].startsWith("\\")) {

            // Trim the leading slash from the first path

            paths[0] = paths[0].substring(1);
        }

        // Find the directory id by traversing the list of directories

        int endIdx = paths.length - 1;
        if (filePath == true)
            endIdx--;

        // If there are no paths to check then return the root directory id

        if (endIdx <= 1 && paths[0].length() == 0) {
            int[] ids = new int[1];
            ids[0] = 0;
            return ids;
        }

        // Allocate the directory id list

        int[] ids = new int[paths.length];
        for (int i = 0; i < ids.length; i++)
            ids[i] = -1;

        // Build up the current path as we traverse the list

        StringBuffer pathStr = new StringBuffer("\\");

        // Check for paths in the file state cache

        FileStateCache cache = ctx.getStateCache();
        FileState fstate = null;

        // Traverse the path list, initialize the directory id to the root id

        int dirId = 0;
        int parentId = -1;
        int idx = 0;

        try {

            // Loop until the end of the path list

            while (idx <= endIdx) {

                // Get the current path, and add to the full path string

                String curPath = paths[idx];
                pathStr.append(curPath);
                // log4j.debug("DBD#findParentDirectoryIdList() idx:"
                // +idx+" , curPath:"+curPath+" ,pathStr:"+pathStr+", parentId:"+parentId+",dirId:"+dirId);
                // Check if there is a file state for the current path

                fstate = cache.findFileState(pathStr.toString());

                if (fstate != null && fstate.getFileId() != -1) {

                    // Get the file id from the cached information

                    ids[idx] = fstate.getFileId();
                    parentId = dirId;
                    dirId = ids[idx];
                } else {

                    // Search for the current directory in the database

                    parentId = dirId;

                    dirId = ctx.getDBInterface().getFileId(dirId, curPath, true, true, userId, shareName, userName);

                    if (dirId != -1) {

                        // Get the next directory id

                        ids[idx] = dirId;

                        // Check if we have a file state, or create a new file
                        // state for the current path

                        if (fstate != null) {

                            // Set the file id for the file state

                            fstate.setFileId(dirId);
                        } else {

                            // Create a new file state for the current path

                            fstate = cache.findFileState(pathStr.toString(), true);

                            // Get the file information

                            DBFileInfo finfo = ctx.getDBInterface().getFileInformation(parentId, dirId,
                                    DBInterface.FileAll, userId, shareName, userName);
                            fstate.addAttribute(FileState.FileInformation, finfo);
                            fstate.setFileStatus(
                                    finfo.isDirectory() ? FileStatus.DirectoryExists : FileStatus.FileExists);
                            fstate.setFileId(dirId);
                        }
                    } else {
                        // log4j.warn("DBD#findParentDirectoryIdList  dirId = ctx.getDBInterface().getFileId  dirId=-1 ;dirId:"+dirId+",curPath:"+curPath);
                        return null;
                    }
                }

                // Update the path index

                idx++;

                // Update the current path string

                pathStr.append("\\");
            }
        } catch (DBException ex) {
            log4j.error(ex);
            return null;
        }

        // Return the directory id list

        return ids;
    }

    /**
     * Return file information about the specified file, using the internal file
     * id
     * 
     * @param path
     *            String
     * @param dirId
     *            int
     * @param fid
     *            int
     * @param dbCtx
     *            DBDeviceContext
     * @return DBFileInfo
     * @exception IOException
     */
    public DBFileInfo getFileInfo(String path, int dirId, int fid, DBDeviceContext dbCtx, String userName,
            String shareName, int userId) {

        // Check if the file is in the cache
        FileState state = getFileState(path, dbCtx, true);

        if (state != null && state.getFileId() != -1) {

            // Debug
            //log4j.debug("@@ Cache hit - getFileInfo() path=" + path);

            // Return the file information

            DBFileInfo finfo = (DBFileInfo) state.findAttribute(FileState.FileInformation);
            if (finfo != null)
                return finfo;
        }

        // Get the file information from the database

        DBFileInfo finfo = null;

        try {

            // Get the file information

            finfo = dbCtx.getDBInterface().getFileInformation(dirId, fid, DBInterface.FileAll, userId, shareName,
                    userName);
            // log4j.debug("DBD#getFileInfo ; dirId:" +
            // dirId+" , fid:"+fid+", userId: "+userId+", shareName :"+shareName);
        } catch (DBException ex) {
            log4j.error(ex);
            finfo = null;
        }

        // Set the full path for the file

        if (finfo != null)
            finfo.setFullName(path);

        // Update the cached information, if available

        if (state != null && finfo != null) {
            state.addAttribute(FileState.FileInformation, finfo);
            state.setFileStatus(finfo.isDirectory() ? FileStatus.DirectoryExists : FileStatus.FileExists);
        }

        // Return the file information

        return finfo;
    }

    /**
     * Get the details for a file stream
     * 
     * @param parent
     *            FileState
     * @param paths
     *            String[]
     * @param dbCtx
     *            DBDeviceContext
     * @return DBFileInfo
     */
    public DBFileInfo getStreamInfo(FileState parent, String[] paths, DBDeviceContext dbCtx) {

        // Check if the file is in the cache

        String streamPath = paths[0] + paths[1] + paths[2];
        FileState state = getFileState(streamPath, dbCtx, true);

        if (state != null && state.getFileId() != -1) {

            // Debug
            log4j.debug("@@ Cache hit - getStreamInfo() path=" + streamPath);

            // Return the file information

            DBFileInfo finfo = (DBFileInfo) state.findAttribute(FileState.FileInformation);
            if (finfo != null)
                return finfo;
        }

        // DEBUG

        log4j.debug("DBD#getStreamInfo parent=" + parent.getPath() + ", stream=" + paths[2]);

        // Get a list of the streams for the parent file

        DBFileInfo finfo = null;

        try {

            // Get the list of streams

            StreamInfoList sList = (StreamInfoList) parent.findAttribute(DBStreamList);

            if (sList == null) {

                // No cached stream information, get the list from the database

                sList = dbCtx.getDBInterface().getStreamsList(parent.getFileId(), DBInterface.StreamAll);

                // Cache the information

                parent.addAttribute(DBStreamList, sList);
            }

            // Find the required stream information

            if (sList != null) {

                // Find the required stream information

                StreamInfo sInfo = sList.findStream(paths[2]);

                // Convert the stream information to file information

                if (sInfo != null) {

                    // Load the stream information

                    finfo = new DBFileInfo();
                    finfo.setFileId(parent.getFileId());

                    // Copy the stream information

                    finfo.setFileName(sInfo.getName());
                    finfo.setSize(sInfo.getSize());

                    // Get the file creation date, or use the current date/time

                    if (sInfo.hasCreationDateTime())
                        finfo.setCreationDateTime(sInfo.getCreationDateTime());

                    // Get the modification date, or use the current date/time

                    if (sInfo.hasModifyDateTime())
                        finfo.setModifyDateTime(sInfo.getModifyDateTime());
                    else if (sInfo.hasCreationDateTime())
                        finfo.setModifyDateTime(sInfo.getCreationDateTime());

                    // Get the last access date, or use the current date/time

                    if (sInfo.hasAccessDateTime())
                        finfo.setAccessDateTime(sInfo.getAccessDateTime());
                    else if (sInfo.hasCreationDateTime())
                        finfo.setAccessDateTime(sInfo.getCreationDateTime());
                }
            }
        } catch (DBException ex) {
            log4j.error(ex);
            finfo = null;
        }

        // Set the full path for the file

        if (finfo != null)
            finfo.setFullName(streamPath);

        // Update the cached information, if available

        if (state != null && finfo != null) {
            state.addAttribute(FileState.FileInformation, finfo);
            state.setFileStatus(FileStatus.FileExists);
        }

        // Return the file information for the stream

        return finfo;
    }

    /**
     * Get the cached file state for the specified path
     * 
     * @param path
     *            String
     * @param ctx
     *            DBDeviceContext
     * @param create
     *            boolean
     * @return FileState
     */
    protected final FileState getFileState(String path, DBDeviceContext ctx, boolean create) {

        // Access the file state cache

        FileStateCache cache = ctx.getStateCache();
        if (cache == null)
            return null;

        // Return the required file state
        return cache.findFileState(path, create);
    }

    /**
     * Connection opened to this disk device
     * 
     * @param sess
     *            Server session
     * @param tree
     *            Tree connection
     */
    public void treeOpened(SrvSession sess, TreeConnection tree) {
    }

    /**
     * Connection closed to this device
     * 
     * @param sess
     *            Server session
     * @param tree
     *            Tree connection
     */
    public void treeClosed(SrvSession sess, TreeConnection tree) {
    }

    /**
     * Check if general debug output is enabled
     * 
     * @return boolean
     */
    protected final boolean hasDebug() {
        return m_debug;
    }

    /**
     * Return disk information about a shared filesystem
     * 
     * @param ctx
     *            DiskDeviceContext
     * @param info
     *            SrvDiskInfo
     * @exception IOException
     */
    public final void getDiskInformation(DiskDeviceContext ctx, SrvDiskInfo info) throws IOException {

        // Check if there is static disk size information available

        if (ctx.hasDiskInformation())
            info.copyFrom(ctx.getDiskInformation());

        // Check that the context is a JDBC context

        if (ctx instanceof DBDeviceContext) {

            // Access the associated file loader class, if it implements the
            // disk size interface then call the loader
            // to fill in the disk size details

            DBDeviceContext dbCtx = (DBDeviceContext) ctx;

            if (dbCtx.getFileLoader() instanceof DiskSizeInterface) {

                // Pass the disk size request to the associated file loader

                DiskSizeInterface sizeInterface = (DiskSizeInterface) dbCtx.getFileLoader();

                sizeInterface.getDiskInformation(ctx, info);

                // DEBUG
                log4j.debug("DBD#getDiskInformation() handed to file loader");
            }
        }

        // Check if there is a quota manager, if so then get the current free
        // space from the quota manager

        if (ctx.hasQuotaManager()) {

            // Get the free space, in bytes, from the quota manager

            long freeSpace = ctx.getQuotaManager().getAvailableFreeSpace();

            // Convert the free space to free units

            long freeUnits = freeSpace / info.getUnitSize();
            info.setFreeUnits(freeUnits);

            // DEBUG
            log4j.debug("DBD#getDiskInformation() freeSpace:" + freeSpace + " , freeUnits:" + freeUnits);
        }
    }

    /**
     * Determine if NTFS streams support is enabled. Check if the associated
     * file loader supports NTFS streams.
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @return boolean
     */
    public boolean hasStreamsEnabled(SrvSession sess, TreeConnection tree) {

        if (1 == 1) {
            return false;// ????xls??
        }
        // Check that the context is a JDBC context

        if (tree.getContext() instanceof DBDeviceContext) {

            // Access the associated file loader class to check if it supports
            // NTFS streams

            DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
            if (dbCtx.hasNTFSStreamsEnabled()) {

                // Check if the file loader supports NTFS streams

                return dbCtx.getFileLoader().supportsStreams();
            }
        }

        // Disable streams

        return false;
    }

    /**
     * Get the stream information for the specified file stream
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param streamInfo
     *            StreamInfo
     * @return StreamInfo
     * @exception IOException
     */
    public StreamInfo getStreamInformation(SrvSession sess, TreeConnection tree, StreamInfo streamInfo)
            throws IOException {

        // DEBUG
        log4j.debug("DBD#getStreamInformation() called ###");

        return null;
    }

    /**
     * Return the list of available streams for the specified file
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param fileName
     *            String
     * @return StreamInfoList
     * @exception IOException
     */
    public StreamInfoList getStreamList(SrvSession sess, TreeConnection tree, String fileName) throws IOException {

        // Access the JDBC context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Get the file state for the top level file

        String parentPath = FileName.getParentPathForStream(fileName);
        FileState fstate = getFileState(parentPath, dbCtx, true);

        // DEBUG
        log4j.debug("DBD#getStreamList() fileName=" + fileName + ", userName:" + userName);

        // Check if the file state already has the streams list cached

        StreamInfoList streamList = (StreamInfoList) fstate.findAttribute(DBStreamList);
        if (streamList != null && streamList.numberOfStreams() > 0)
            return streamList;

        // Get the main file information and convert to stream information

        String shareName = DiskUtil.findFristPath(fileName);

        DBFileInfo finfo = getFileDetails(fileName, dbCtx, fstate, userName, shareName, userId);
        if (finfo == null) {
            log4j.warn("DBD#getStreamList() getFileDetails  finfo==null; fileName=" + fileName + ", userName:"
                    + userName);
            return null;
        }

        // Create the stream list

        streamList = new StreamInfoList();

        // Add an entry for the main file data stream

        StreamInfo sinfo = new StreamInfo("::$DATA", finfo.getFileId(), 0, finfo.getSize(),
                finfo.getAllocationSize());
        streamList.addStream(sinfo);

        // Get the list of streams

        StreamInfoList sList = loadStreamList(fstate, finfo, dbCtx, true);
        if (sList != null) {

            // Copy the stream information to the main list

            for (int i = 0; i < sList.numberOfStreams(); i++) {

                // Add the stream to the main list

                streamList.addStream(sList.getStreamAt(i));
            }
        }

        // Cache the stream list

        fstate.addAttribute(DBStreamList, streamList);

        // Return the stream list

        return streamList;
    }

    /**
     * Create a new stream with the specified parent file
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param params
     *            FileOpenParams
     * @param parent
     *            FileState
     * @param dbCtx
     *            DBDeviceContext
     * @return NetworkFile
     * @exception IOException
     */
    protected final NetworkFile createStream(SrvSession sess, TreeConnection tree, FileOpenParams params,
            FileState parent, DBDeviceContext dbCtx) throws IOException {

        // Get the file information for the parent file
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        String shareName = DiskUtil.findFristPath(params.getPath());
        DBFileInfo finfo = getFileDetails(params.getPath(), dbCtx, parent, userName, shareName, userId);

        if (finfo == null) {
            log4j.warn("DBD#createStream() AccessDeniedException  parent:" + parent);
            throw new AccessDeniedException();
        }

        // Get the list of streams for the file

        StreamInfoList streamList = (StreamInfoList) parent.findAttribute(DBStreamList);
        if (streamList == null)
            streamList = getStreamList(sess, tree, params.getPath());

        if (streamList == null) {
            log4j.warn("DBD#createStream() AccessDeniedException  parent:" + parent);
            throw new AccessDeniedException();
        }

        // Check if the stream already exists

        if (streamList.findStream(params.getStreamName()) != null) {
            log4j.error("Stream exists, " + params.getFullPath());
            throw new FileExistsException("Stream exists, " + params.getFullPath());
        }

        // Create a new stream record

        DBNetworkFile file = null;
        FileAccessToken accessToken = null;
        FileState fstate = null;

        try {

            // Check if the file can be opened in the requested mode

            fstate = getFileState(params.getFullPath(), dbCtx, true);
            accessToken = dbCtx.getStateCache().grantFileAccess(params, fstate, FileStatus.FileExists);

            // Create a new stream record

            int stid = dbCtx.getDBInterface().createStreamRecord(params.getStreamName(), finfo.getFileId());

            // Create a network file to hold details of the new stream

            file = (DBNetworkFile) dbCtx.getFileLoader().openFile(params, finfo.getFileId(), stid,
                    finfo.getDirectoryId(), true, false, userName);
            file.setFullName(params.getFullPath());
            file.setStreamId(stid);
            file.setStreamName(params.getStreamName());
            file.setDirectoryId(finfo.getDirectoryId());
            file.setAttributes(params.getAttributes());

            // Set the file state for the file

            file.setFileState(dbCtx.getStateCache().getFileStateProxy(fstate));

            // Store the access token

            file.setAccessToken(accessToken);

            // Open the stream file

            file.openFile(true);

            // Add an entry to the stream list for the new stream

            StreamInfo stream = new StreamInfo(params.getStreamName(), file.getFileId(), stid);
            streamList.addStream(stream);

            // DEBUG
            log4j.debug("DBD#createStream() file=" + params.getPath() + ", stream=" + params.getStreamName()
                    + ", fid/stid=" + file.getFileId() + "/" + stid);
        } catch (DBException ex) {
            log4j.error("Error: " + ex.toString());
        } finally {

            // Check if the stream file is not valid but an access token has
            // been allocated

            if (file == null && accessToken != null)
                dbCtx.getStateCache().releaseFileAccess(fstate, accessToken);
        }

        // If the file/stream is not valid throw an exception

        if (file == null) {
            log4j.error("AccessDeniedException ,fullPath:" + params.getFullPath());
            throw new AccessDeniedException(params.getFullPath());
        }

        // Return the network file for the new stream
        return file;
    }

    /**
     * Open an existing stream with the specified parent file
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param params
     *            FileOpenParams
     * @param parent
     *            FileState
     * @param dbCtx
     *            DBDeviceContext
     * @return NetworkFile
     * @exception IOException
     */
    protected final NetworkFile openStream(SrvSession sess, TreeConnection tree, FileOpenParams params,
            FileState parent, DBDeviceContext dbCtx) throws IOException {

        // Get the file information for the parent file
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        String shareName = DiskUtil.findFristPath(params.getPath());
        DBFileInfo finfo = getFileDetails(params.getPath(), dbCtx, parent, userName, shareName, userId);

        if (finfo == null) {
            log4j.warn("DBD#openStream() AccessDeniedException  parent:" + parent);
            throw new AccessDeniedException();
        }

        // Get the list of streams for the file

        StreamInfoList streamList = getStreamList(sess, tree, params.getPath());
        if (streamList == null) {
            log4j.warn("DBD#openStream() AccessDeniedException  parent:" + parent);
            throw new AccessDeniedException();
        }

        // Check if the stream exists

        StreamInfo sInfo = streamList.findStream(params.getStreamName());
        if (sInfo == null) {
            log4j.error("FileNotFoundException Stream does not exist, " + params.getFullPath());
            throw new FileNotFoundException("Stream does not exist, " + params.getFullPath());
        }

        // Open the stream

        DBNetworkFile jdbcFile = null;
        FileAccessToken accessToken = null;
        FileState fstate = null;
        log4j.debug("DBD#openStream() file=" + params.getPath() + ", stream=" + sInfo.getName());
        try {

            // Get, or create, a file state for the stream

            fstate = getFileState(params.getFullPath(), dbCtx, true);

            // Check if the file stream can be opened in the requested mode

            accessToken = dbCtx.getStateCache().grantFileAccess(params, fstate, FileStatus.FileExists);

            // Check if the file shared access indicates exclusive file access

            if (params.getSharedAccess() == SharingMode.NOSHARING && fstate.getOpenCount() > 0
                    && params.getProcessId() != fstate.getProcessId())
                throw new FileSharingException("File already open, " + params.getPath());

            // Set the file information for the stream, using the stream
            // information

            DBFileInfo sfinfo = new DBFileInfo(sInfo.getName(), params.getFullPath(), finfo.getFileId(),
                    finfo.getDirectoryId());

            sfinfo.setFileSize(sInfo.getSize());
            sfinfo.setFileAttributes(FileAttribute.Normal);

            sfinfo.setCreationDateTime(sInfo.getCreationDateTime());
            sfinfo.setModifyDateTime(sInfo.getModifyDateTime());
            sfinfo.setAccessDateTime(sInfo.getAccessDateTime());

            fstate.addAttribute(FileState.FileInformation, sfinfo);

            // Create a JDBC network file and open the stream
            log4j.debug("Create a JDBC network file and open the stream");

            jdbcFile = (DBNetworkFile) dbCtx.getFileLoader().openFile(params, finfo.getFileId(),
                    sInfo.getStreamId(), finfo.getDirectoryId(), false, false, userName);

            jdbcFile.setFileState(dbCtx.getStateCache().getFileStateProxy(fstate));
            jdbcFile.setFileDetails(sfinfo);

            // Open the stream file, if not a directory

            jdbcFile.openFile(false);
        } finally {

            // Check if the stream file is not valid but an access token has
            // been allocated

            if (jdbcFile == null && accessToken != null)
                dbCtx.getStateCache().releaseFileAccess(fstate, accessToken);
        }

        // Return the network file

        return jdbcFile;
    }

    /**
     * Close an NTFS stream
     * 
     * @param sess
     *            Session details
     * @param tree
     *            Tree connection
     * @param stream
     *            Network file details
     * @exception IOException
     */
    protected final void closeStream(SrvSession sess, TreeConnection tree, NetworkFile stream) throws IOException {

        // Debug
        log4j.debug("DBD#closeStream() file=" + stream.getFullName() + ", stream=" + stream.getStreamName()
                + ", fid/stid=" + stream.getFileId() + "/" + stream.getStreamId());

        // Access the JDBC context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();

        // Close the file

        dbCtx.getFileLoader().closeFile(sess, stream);

        // Access the JDBC file

        DBNetworkFile jdbcFile = null;

        if (stream instanceof DBNetworkFile) {

            // Access the JDBC file

            jdbcFile = (DBNetworkFile) stream;

            // Decrement the open file count

            FileState fstate = jdbcFile.getFileState();

            // Check if the file state is valid, if not then check the main file
            // state cache

            if (fstate == null) {

                // Check the main file state cache

                fstate = getFileState(stream.getFullName(), dbCtx, false);
            }

            // Release the file access token for the stream

            if (jdbcFile.hasAccessToken()) {

                // Release the access token, update the open file count

                dbCtx.getStateCache().releaseFileAccess(fstate, jdbcFile.getAccessToken());
            }

            // Check if we have a valid file state

            if (fstate != null) {

                // Update the cached file size

                FileInfo finfo = getFileInformation(sess, tree, fstate.getPath());
                log4j.debug("DBDiskDriver.closeStream ; path:" + fstate.getPath() + " , fstate:" + fstate);
                if (finfo != null && stream.getWriteCount() > 0) {

                    // Update the file size

                    finfo.setSize(jdbcFile.getFileSize());

                    // Update the modified date/time

                    finfo.setModifyDateTime(jdbcFile.getModifyDate());

                    // DEBUG
                    log4j.debug(
                            "  Stream size=" + jdbcFile.getFileSize() + ", modifyDate=" + jdbcFile.getModifyDate());
                }
            }
        } else
            log4j.debug("closeFile() Not DBNetworkFile file=" + stream);

        // Check if the stream was opened for write access, if so then update
        // the stream size

        if (stream.getGrantedAccess() != NetworkFile.READONLY && stream.isDirectory() == false
                && stream.getWriteCount() > 0) {

            // DEBUG
            log4j.debug("  Update stream size=" + stream.getFileSize());

            // Get the current date/time

            long modifiedTime = 0L;
            if (stream.hasModifyDate())
                modifiedTime = stream.getModifyDate();
            else
                modifiedTime = System.currentTimeMillis();

            // Check if the modified time is earlier than the file creation
            // date/time

            if (stream.hasCreationDate() && modifiedTime < stream.getCreationDate()) {

                // Use the creation date/time for the modified date/time

                modifiedTime = stream.getCreationDate();

                // DEBUG
                log4j.debug("Close stream using creation date/time for modified date/time");
            }

            // Update the in-memory stream information

            String parentPath = FileName.getParentPathForStream(stream.getFullName());
            FileState parent = getFileState(parentPath, dbCtx, false);
            StreamInfo sInfo = null;
            int sattr = 0;

            if (parent != null) {

                // Check if the stream list has been loaded

                StreamInfoList streamList = getStreamList(sess, tree, parentPath);
                if (streamList != null) {

                    // Find the stream information

                    sInfo = streamList.findStream(stream.getStreamName());
                    if (sInfo != null) {

                        // Update the stream size

                        sInfo.setSize(stream.getFileSize());
                        sattr += StreamInfo.SetStreamSize;

                        // DEBUG
                        log4j.debug("Updated stream file size");
                    } else
                        log4j.error("** Failed to find details for stream " + stream.getStreamName());
                } else
                    log4j.error("** Failed to get streams list for " + parentPath);
            }

            // Update the file details for the file stream in the database

            try {

                // Check if the file strea, details are valid

                if (sInfo == null) {

                    // Create the stream information

                    sInfo = new StreamInfo();

                    sInfo.setSize(stream.getFileSize());
                    sInfo.setStreamId(stream.getStreamId());

                    sattr += StreamInfo.SetStreamSize;
                }

                // Set the modify date/time for the stream

                sInfo.setModifyDateTime(System.currentTimeMillis());
                sattr += StreamInfo.SetModifyDate;

                // Set the stream information values to be updated

                sInfo.setStreamInformationFlags(sattr);

                // Update the stream details

                dbCtx.getDBInterface().setStreamInformation(stream.getDirectoryId(), stream.getFileId(),
                        stream.getStreamId(), sInfo);
            } catch (DBException ex) {
                log4j.error(ex);
            }
        }
    }

    /**
     * Delete a stream within a file
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param name
     *            String
     * @exception IOException
     */
    protected final void deleteStream(SrvSession sess, TreeConnection tree, String name) throws IOException {

        // Access the JDBC context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        String shareName = DiskUtil.findFristPath(name, tree.getFullName());
        log4j.debug("DBD#deleteStream ; name:" + name + " , shareName:" + shareName + ", userName:" + userName);
        // Get, or create, the file state for main file path and stream

        String filePath = FileName.getParentPathForStream(name);
        FileState fstate = getFileState(filePath, dbCtx, true);
        FileState sstate = getFileState(name, dbCtx, false);

        // Check if the file is within an active retention period

        if (fstate.hasActiveRetentionPeriod()) {
            log4j.error("AccessDeniedException File retention active");
            throw new AccessDeniedException("File retention active");
        }

        // Get the top level file information

        DBFileInfo finfo = getFileDetails(filePath, dbCtx, fstate, userName, shareName, userId);

        // Get the stream list for the top level file

        StreamInfoList streamList = (StreamInfoList) fstate.findAttribute(DBStreamList);
        if (streamList == null)
            streamList = getStreamList(sess, tree, filePath);

        if (streamList == null) {
            log4j.error("FileNotFoundException Stream not found, " + name);
            throw new FileNotFoundException("Stream not found, " + name);
        }

        // Parse the path string to get the directory, file name and stream name

        String[] paths = FileName.splitPathStream(name);

        // Find the required stream details

        StreamInfo sInfo = streamList.findStream(paths[2]);
        if (sInfo == null) {
            log4j.error("FileNotFoundException Stream not found, " + name);
            throw new FileNotFoundException("Stream not found, " + name);
        }

        // Delete the stream record from the database

        try {

            // Call the file loader to delete the stream data

            dbCtx.getFileLoader().deleteFile(name, sInfo.getFileId(), sInfo.getStreamId(), shareName);

            // Delete the stream record

            dbCtx.getDBInterface().deleteStreamRecord(sInfo.getFileId(), sInfo.getStreamId(),
                    dbCtx.isTrashCanEnabled());

            // Remove the stream information from the in memory list

            streamList.removeStream(sInfo.getName());

            // Set the streams file state to indicate that it does not exist

            if (sstate != null)
                sstate.setFileStatus(FileStatus.NotExist);
        } catch (DBException ex) {
            log4j.error("Error: " + ex.toString());
        }
    }

    /**
     * Load the stream list for the specified file
     * 
     * @param fstate
     *            FileState
     * @param finfo
     *            DBFileInfo
     * @param dbCtx
     *            DBDeviceContext
     * @param dbLoad
     *            boolean
     * @return StreamInfoList
     */
    protected final StreamInfoList loadStreamList(FileState fstate, DBFileInfo finfo, DBDeviceContext dbCtx,
            boolean dbLoad) {

        // Check if the stream list has already been loaded

        StreamInfoList sList = (StreamInfoList) fstate.findAttribute(FileState.StreamsList);

        // If the streams list is not loaded then load it from the database

        if (sList == null && dbLoad == true) {

            // Load the streams list from the database

            try {

                // Load the streams list

                sList = dbCtx.getDBInterface().getStreamsList(finfo.getFileId(), DBInterface.StreamAll);

                // Cache the streams list via the parent file state

                if (sList != null)
                    fstate.addAttribute(DBStreamList, sList);
            } catch (DBException ex) {
                log4j.error(ex);
            }
        }

        // Return the streams list

        return sList;
    }

    /**
     * Rename a stream
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param oldName
     *            String
     * @param newName
     *            String
     * @param overWrite
     *            boolean
     * @exception IOException
     */
    public void renameStream(SrvSession sess, TreeConnection tree, String oldName, String newName,
            boolean overWrite) throws IOException {
    }

    /**
     * Return the volume information
     * 
     * @param ctx
     *            DiskDeviceContext
     * @return VolumeInfo
     */
    public VolumeInfo getVolumeInformation(DiskDeviceContext ctx) {

        // Check if the context has volume information

        VolumeInfo volInfo = ctx.getVolumeInformation();

        if (volInfo == null) {

            // Create volume information for the filesystem

            volInfo = new VolumeInfo(ctx.getDeviceName());

            // Add to the device context

            ctx.setVolumeInformation(volInfo);
        }

        // Check if the serial number is valid

        if (volInfo.getSerialNumber() == 0) {

            // Generate a random serial number

            volInfo.setSerialNumber(new java.util.Random().nextInt());
        }

        // Check if the creation date is valid

        if (volInfo.hasCreationDateTime() == false) {

            // Set the creation date to now

            volInfo.setCreationDateTime(new java.util.Date());
        }

        // Return the volume information

        return volInfo;
    }

    /**
     * Return the lock manager implementation
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @return LockManager
     */
    public LockManager getLockManager(SrvSession sess, TreeConnection tree) {

        // Access the context
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        return dbCtx.getFileStateLockManager();
    }

    /**
     * Return the oplock manager implementation
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @return OpLockManager
     */
    public OpLockManager getOpLockManager(SrvSession sess, TreeConnection tree) {

        // Access the context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        return dbCtx.getFileStateLockManager();
    }

    /**
     * Enable/disable oplock support
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @return boolean
     */
    public boolean isOpLocksEnabled(SrvSession sess, TreeConnection tree) {

        // Access the context

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        return dbCtx.isOpLocksEnabled();
    }

    /**
     * Convert a file id to a share relative path
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param dirid
     *            int
     * @param fileid
     * @return String
     * @exception FileNotFoundException
     */
    public String buildPathForFileId(SrvSession sess, TreeConnection tree, int dirid, int fileid)
            throws FileNotFoundException {

        // Access the JDBC context
        String userName = sess.getClientInformation().getUserName();
        String shareName = tree.getContext().getShareName();
        log4j.error("\n\n\nDBD#buildPathForFileId ; shareName ??, shareName:" + shareName
                + ", userName:" + userName + "\n\n\n");

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        // Build an array of folder names working back from the files id

        ArrayList<String> names = new ArrayList<String>(16);
        log4j.debug("DBD#buildPathForFileId ; dirid:" + dirid + " , fileid:" + fileid + " , shareName:" + shareName
                + ", userName:" + userName);
        try {

            int userId = sess.getClientInformation().getUid();

            // Loop, walking backwards up the tree until we hit root

            int curFid = fileid;
            int curDid = dirid;

            FileInfo finfo = null;

            do {

                // Search for the current file in the database
                log4j.debug("do: curDid:" + curDid + " , curFid:" + curFid);
                finfo = dbCtx.getDBInterface().getFileInformation(curDid, curFid, DBInterface.FileIds, userId,
                        shareName, userName);

                if (finfo != null) {
                    // Get the filename

                    names.add(finfo.getFileName());

                    // The directory id becomes the next file id to search for

                    curFid = finfo.getDirectoryId();
                    curDid = -1;
                } else {
                    log4j.warn("FileNotFoundException ; curFid:" + curFid);
                    throw new FileNotFoundException("" + curFid);
                }

            } while (curFid > 0);
        } catch (DBException ex) {
            log4j.error(ex);
            return null;
        }

        // Build the path string

        StringBuffer pathStr = new StringBuffer(256);
        pathStr.append(FileName.DOS_SEPERATOR_STR);

        for (int i = names.size() - 1; i >= 0; i--) {
            pathStr.append(names.get(i));
            pathStr.append(FileName.DOS_SEPERATOR_STR);
        }

        // Remove the trailing slash from the path

        if (pathStr.length() > 0)
            pathStr.setLength(pathStr.length() - 1);

        // Return the path string

        return pathStr.toString();
    }

    /**
     * Determine if symbolic links are enabled
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @return boolean
     */
    public boolean hasSymbolicLinksEnabled(SrvSession sess, TreeConnection tree) {

        // Access the associated database interface to check if it supports
        // symbolic links

        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        if (dbCtx.getDBInterface().supportsFeature(DBInterface.FeatureSymLinks)) {

            // Database interface supports symbolic links

            return true;
        }

        // Symbolic links not supported

        return false;
    }

    /**
     * Read the link data for a symbolic link
     * 
     * @param sess
     *            SrvSession
     * @param tree
     *            TreeConnection
     * @param path
     *            String
     * @return String
     * @exception AccessDeniedException
     * @exception FileNotFoundException
     */
    public String readSymbolicLink(SrvSession sess, TreeConnection tree, String path)
            throws AccessDeniedException, FileNotFoundException {

        // Access the associated database interface to check if it supports
        // symbolic links
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        DBInterface dbInterface = dbCtx.getDBInterface();
        String symLink = null;

        String shareName = DiskUtil.findFristPath(path);

        if (dbInterface.supportsFeature(DBInterface.FeatureSymLinks)) {

            // Get, or create, the file state for the existing file

            FileState fstate = getFileState(path, dbCtx, true);

            // Get the file id of the existing file

            int fid = fstate.getFileId();
            int dirId = -1;

            if (fid == -1) {
                // Split the current path string and find the file id of the
                // existing file/directory

                dirId = findParentDirectoryId(dbCtx, path, true, userName, shareName, userId);
                if (dirId == -1) {
                    log4j.warn("DBD#readSymbolicLink() FileNotFoundException  path:" + path);
                    throw new FileNotFoundException(path);
                }

                // Get the file/directory name

                String[] oldPaths = FileName.splitPath(path);
                String fname = oldPaths[1];

                // Get the file id

                fid = getFileId(path, fname, dirId, dbCtx, userId, shareName, userName);
                if (fid == -1) {
                    log4j.error("FileNotFoundException " + path);
                    throw new FileNotFoundException(path);
                }

                // Update the file state

                fstate.setFileId(fid);
            }

            try {

                // Database interface supports symbolic links, read the symbolic
                // link

                symLink = dbInterface.readSymbolicLink(dirId, fid);
            } catch (DBException ex) {
                log4j.warn("DBD#readSymbolicLink() FileNotFoundException  path:" + path, ex);
                throw new FileNotFoundException(path);
            }
        }

        // Return the symbolic link data

        return symLink;
    }

    /**
     * Return the security descriptor length for the specified file
     * 
     * @param sess
     *            Server session
     * @param tree
     *            Tree connection
     * @param netFile
     *            Network file
     * @return int
     * @exception SMBSrvException
     */
    public int getSecurityDescriptorLength(SrvSession sess, TreeConnection tree, NetworkFile netFile)
            throws SMBSrvException {

        return 0;
    }

    /**
     * Load a security descriptor for the specified file
     * 
     * @param sess
     *            Server session
     * @param tree
     *            Tree connection
     * @param netFile
     *            Network file
     * @return SecurityDescriptor
     * @exception SMBSrvException
     */
    public SecurityDescriptor loadSecurityDescriptor(SrvSession sess, TreeConnection tree, NetworkFile netFile)
            throws SMBSrvException {

        return null;
    }

    /**
     * Save the security descriptor for the specified file
     * 
     * @param sess
     *            Server session
     * @param tree
     *            Tree connection
     * @param netFile
     *            Network file
     * @param secDesc
     *            Security descriptor
     * @exception SMBSrvException
     */
    public void saveSecurityDescriptor(SrvSession sess, TreeConnection tree, NetworkFile netFile,
            SecurityDescriptor secDesc) throws SMBSrvException {

    }

    public int OfffileExists(SrvSession sess, TreeConnection tree, String name, int createOptn) {
        // Access the JDBC context
        String userName = sess.getClientInformation().getUserName();
        int userId = sess.getClientInformation().getUid();
        // String shareName = tree.getContext().getShareName();
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        // debug
        // log4j.debug("DBD#fileExists name=" + name);
        // Check if the path contains an NTFS stream name

        int fileSts = FileStatus.NotExist;
        FileState fstate = null;//DBUtil.SHARENAME_COMMFILE;
        String shareName = DiskUtil.findFristPath(name, tree.getFullName());

        // Check for the root directory
        if (name.length() == 0 || name.compareTo("\\") == 0) {
            // log4j.debug("DBD#fileExists  ,  DirectoryExists ");
            return FileStatus.DirectoryExists;
        }
        if (name.equalsIgnoreCase("\\" + userName + DBUtil.SPECIAL_CHAR)) {
            // log4j.debug("DBD#fileExists username@ ,  DirectoryExists ");
            return FileStatus.DirectoryExists;
        }
        // if(name.toLowerCase().startsWith("\\"+userName.toLowerCase()+DBUtil.SPECIAL_CHAR)==false)
        // {
        // log4j.warn("DBD#fileExists ??name? ,  NotExist ; name:"+name);
        // return FileStatus.NotExist;
        // }
        // ????
        // (:username@/; username@/)
        //
        if (name.equalsIgnoreCase("\\" + DBUtil.SHARENAME_COMMFILE)
                || name.equalsIgnoreCase("\\" + DBUtil.SHARENAME_USERFILE)
                || name.equalsIgnoreCase("\\" + DBUtil.SHARENAME_RECIVEFILE)) // 
        {
            // log4j.debug("DBD#fileExists username@\ , 
            // DirectoryExists ");
            return FileStatus.DirectoryExists;
        }

        if (FileName.containsStreamName(name)) {

            // Get the file information for the stream
            FileInfo fInfo = null;
            try {
                fInfo = getFileInformation(sess, tree, name);
            } catch (IOException ex) {
                log4j.error(ex);
            }

            // Check if the file information was retrieved for the stream
            if (fInfo != null)
                fileSts = FileStatus.FileExists;

            // Debug
            log4j.debug("DBD#fileExists() nameWithStream=" + name + ", fileSts=" + FileStatus.asString(fileSts));
        } else {

            // Get, or create, the file state for the path

            fstate = getFileState(name, dbCtx, true);

            // Check if the file exists status has been cached

            fileSts = fstate.getFileStatus();

            // if ( fstate.getFileStatus() == FileStatus.Unknown) {
            String ext = DiskUtil.getExt(name);
            if (fstate.getFileStatus() == FileStatus.Unknown
                    || (fstate.getFileStatus() == FileStatus.NotExist && DBUtil.SUPPORT_EXT.contains(ext))) {
                // fstate.getFileStatus()==FileStatus.NotExist
                // &&DBUtil.SUPPORT_EXT.contains(ext) ?

                // Get the file details

                DBFileInfo dbInfo = getFileDetails(name, dbCtx, fstate, userName, shareName, userId);

                if (dbInfo != null) {
                    if (dbInfo.isDirectory() == true)
                        fileSts = FileStatus.DirectoryExists;
                    else
                        fileSts = FileStatus.FileExists;

                    // Save the file id

                    if (dbInfo.getFileId() != -1)
                        fstate.setFileId(dbInfo.getFileId());
                } else {

                    // Indicate that the file does not exist

                    fstate.setFileStatus(FileStatus.NotExist);
                    fileSts = FileStatus.NotExist;
                }

                // Debug
                // log4j.debug("DBD#fileExists() name=" + name + ", fileSts=" +
                // FileStatus.asString(fileSts));
            } else {

                // DEBUG
                // log4j.debug("@@ Cache hit - fileExists() name=" + name +
                // ", fileSts=" + FileStatus.asString(fileSts));
            }
        }

        // Return the file exists status

        return fileSts;
    }

    /**
     * 
     * ?
     * 
     * @param fileVirtualPath
     * @param shareName
     * @param tree
     */
    public void reloadFile(String fileVirtualPath, String shareName, TreeConnection tree) {
        DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        //      log4j.debug("DBDiskLDriver.reloadFile()   hellno ! ");
        //      fileVirtualPath = "\\\\172.20.1.52\\admin@\\\\NAS\\0618\\2010.xlsx";
        //      fileVirtualPath = "\\\\NAS\\0618\\a (2).docx";
        //      fileVirtualPath = "\\\\NAS\\061821\\nsh6.docx";
        //      fileVirtualPath = "\\\\NAS\\061821\\qq.xlsx";
        //      shareName = DBUtil.SHARENAME_COMMFILE;

        if (shareName.equals("2")) {
            fileVirtualPath = "\\" + fileVirtualPath;
        } else if (shareName.equals("1")) {
            fileVirtualPath = "\\" + fileVirtualPath;
        } else {

        }

        DBFileLoader loader = (DBFileLoader) dbCtx.getFileLoader();

        FileState state = getFileState(fileVirtualPath, dbCtx, false);
        if (state != null) {
            //         loader.fileStateExpired(state);
            state.setExpiryTime(0);
            state.removeAttribute(FileState.FileInformation);// 

            //         state.setFileStatus(FileStatus.FileExists);
            //         state.setFileId(finfo.getFileId());
            //         state.setFileSize(finfo.getSize());
            //         // Attach to the file state
            //         state.addAttribute(FileState.FileInformation,
            //               finfo);
            //         dbCtx.getStateCache().renameFileState(
            //               state.getPath(), state, false);// ??(???

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

            if (segInfo != null && segInfo.isQueued() == false && segInfo.hasStatus() != FileSegmentInfo.SaveWait) {
                try {
                    // Delete the temporary file
                    segInfo.deleteTemporaryFile();
                    // Debug
                    log4j.debug("$$ Deleted temporary file " + segInfo.getTemporaryFile() + " [CLOSED] $$");
                } catch (IOException ex) {

                }
            }

            state.removeAllAttributes();

            loader.fileStateExpired(state);
            log4j.info("RELOADFILE OK ! " + fileVirtualPath);
        } else {
            log4j.warn("RELOADFILE ERROR ! Maybe it was already removed! " + fileVirtualPath);
        }
        dbCtx.getStateCache().removeFileState(fileVirtualPath);

        String temporaryFile = "";

        File tempFile = new File(temporaryFile);

        if (tempFile.exists() && tempFile.delete() == false) {

            Debug.println("** Failed to delete " + toString() + " **");

            //throw new IOException("Failed to delete file " + temporaryFile);
        }

        //      final DBDeviceContext dbCtx = (DBDeviceContext) tree.getContext();
        //      log4j.debug("DBDiskLDriver.reloadFile()   hellno ! ");
        ////      fileVirtualPath = "\\\\172.20.1.52\\admin@\\\\NAS\\0618\\2010.xlsx";
        ////      fileVirtualPath = "\\\\NAS\\0618\\a (2).docx";
        ////      fileVirtualPath = "\\\\NAS\\061821\\nsh6.docx";
        ////      fileVirtualPath = "\\\\NAS\\061821\\qq.xlsx";
        ////      shareName = DBUtil.SHARENAME_COMMFILE;
        //      
        //      if (shareName.equals("2")) {
        //         fileVirtualPath = "\\" + fileVirtualPath;
        //      } else if (shareName.equals("1")){
        //         fileVirtualPath = "\\" + fileVirtualPath;
        //      } else {
        //         
        //      }
        //      
        //      final String fileVirtualPath2 = fileVirtualPath;
        //      
        //      final DBFileLoader loader = (DBFileLoader) dbCtx.getFileLoader();
        //      
        //      new Thread() {
        //         @Override
        //         public void run() {
        //            
        //            FileState state = getFileState(fileVirtualPath2, dbCtx, false);
        //            while (state != null) {
        //               loader.fileStateClosed(state);
        //               dbCtx.getStateCache().removeFileState(fileVirtualPath2);
        //               
        //               try {
        //                  Thread.sleep(3000);
        //                  log4j.info("RELOADFILE THREAD.SLEEP(3000) !");
        //               } catch (InterruptedException e) {
        //                  // TODO Auto-generated catch block
        //                  e.printStackTrace();
        //               }
        //               
        //               state = getFileState(fileVirtualPath2, dbCtx, false);
        //            }
        ////            if( state != null ) {
        //////               loader.fileStateExpired(state);
        ////            } else {
        ////               log4j.debug("RELOADFILE ERROR !");
        ////            }
        //         }
        //         
        //      }.start();

    }

}