/* * 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 . */ package org.alfresco.jlan.client; import java.io.*; import java.util.*; import org.alfresco.jlan.client.info.DeviceAttributesInfo; import org.alfresco.jlan.client.info.DeviceInfo; import org.alfresco.jlan.client.info.DiskInfo; import org.alfresco.jlan.client.info.FileInfo; import org.alfresco.jlan.client.info.VolumeInfo; import org.alfresco.jlan.client.smb.DirectoryWatcher; import org.alfresco.jlan.server.filesys.AccessMode; import org.alfresco.jlan.server.filesys.FileAction; import org.alfresco.jlan.server.filesys.FileAttribute; import org.alfresco.jlan.smb.DataType; import org.alfresco.jlan.smb.Dialect; import org.alfresco.jlan.smb.FileInfoLevel; import org.alfresco.jlan.smb.LockingAndX; import org.alfresco.jlan.smb.NTTime; import org.alfresco.jlan.smb.OpLock; import org.alfresco.jlan.smb.PCShare; import org.alfresco.jlan.smb.PacketType; import org.alfresco.jlan.smb.SMBException; import org.alfresco.jlan.smb.SMBStatus; import org.alfresco.jlan.smb.SharingMode; import org.alfresco.jlan.smb.TransactBuffer; import org.alfresco.jlan.smb.WinNT; import org.alfresco.jlan.smb.nt.LoadException; import org.alfresco.jlan.smb.nt.SaveException; import org.alfresco.jlan.smb.nt.SecurityDescriptor; import org.alfresco.jlan.smb.nt.SymLink; import org.alfresco.jlan.util.DataBuffer; import org.alfresco.jlan.util.DataPacker; import org.alfresco.jlan.smb.nt.NTIOCtl; /** * SMB CIFS disk session class * *

* The CIFSDiskSession class extends the DiskSession class and provides CIFS protocol specific * implementations for the DiskSession methods. * *

* An CIFSDiskSession object will be created by the SessionFactory static class when the negotiated * SMB dialect indicates that the remote server supports an SMB dialect greater than Core or * CorePlus. * *

* The SessionFactory.OpenDisk() method is used to create a session to a remote disk share. A * PCShare object specifies the remote server and share to connect to, along with any required * access control. * * @author gkspencer */ public final class CIFSDiskSession extends DiskSession { // Constants // // SMB session keep-alive interval private final static long SessionKeepAlive = 60000L; // List of pending asynchronous requests private List m_asynchRequests; // List of open files with an oplock private HashMap m_oplockFiles; /** * Class constructor * * @param shr Remote server details. * @param dialect SMB dialect that this session is using */ protected CIFSDiskSession(PCShare shr, int dialect) { super(shr, dialect); } /** * Close this connection with the remote server share. * * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public void CloseSession() throws java.io.IOException, SMBException { // Build a tree disconnect packet m_pkt.setCommand(PacketType.TreeDisconnect); m_pkt.setUserId(getUserId()); m_pkt.setTreeId(m_treeid); m_pkt.setParameterCount(0); m_pkt.setByteCount(0); // Send the tree disconnect packet m_pkt.ExchangeSMB(this, m_pkt); // Indicate that the session has been closed m_treeid = DiskSession.Closed; // Close the network session super.CloseSession(); } /** * Create a new directory on the remote file server. * * @param dir Directory name string. If the directory name does not have a leading '\' the * current working directory for this session will be prepended to the string. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final void CreateDirectory(String dir) throws java.io.IOException, SMBException { // Build the new path String newPath = dir; if ( newPath.startsWith("\\") == false) newPath = PCShare.makePath(getWorkingDirectory(), dir); // Pre-NT dialect create directory if ( getDialect() != Dialect.NT || isUnicode() == false) { // Create an SMB create directory packet m_pkt.setCommand(PacketType.CreateDirectory); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setParameterCount(0); // Copy the directory name data block to the SMB packet m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString(newPath, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB create directory packet m_pkt.ExchangeSMB(this, m_pkt, true); } else { // Use the NTCreateAndX SMB to create the directory CIFSFile dirFile = NTCreate(newPath, AccessMode.NTRead, FileAttribute.NTDirectory, SharingMode.READWRITE, FileAction.NTCreate, 0, WinNT.CreateDirectory); // Close the directory file dirFile.Close(); } } /** * Create and open a file on the remote file server. * * @param fname Remote file name string. * @return SMBFile for the opened file, else null. * @exception java.io.IOException If an I/O error occurs * @exception SMBException If an SMB error occurs */ public final SMBFile CreateFile(String fname) throws java.io.IOException, SMBException { // Create a new file return OpenFile(fname, AccessMode.WriteOnly); } /** * Delete the specified directory on the remote file server. * * @param dir Directory name string. If the directory name does not have a leading '\' the * current working directory for this session will be preprended to the string. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final void DeleteDirectory(String dir) throws java.io.IOException, SMBException { // Create an SMB delete directory packet m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setCommand(PacketType.DeleteDirectory); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setParameterCount(0); // Check if the directory name contains a path String delPath = dir; if ( delPath.startsWith("\\") == false) delPath = PCShare.makePath(getWorkingDirectory(), dir); // Pack the path to be deleted m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString(delPath, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB delete directory packet m_pkt.ExchangeSMB(this, m_pkt, true); } /** * Delete the specified file on the remote file server. * * @param fname File name of the remote file to delete. If the file name does not have a leading * '\' the current working directory for this session will be prepended to the * string. * @param attr File attributes of the file(s) to delete. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final void DeleteFile(String fname, int attr) throws java.io.IOException, SMBException { // Create an SMB delete file packet m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setCommand(PacketType.DeleteFile); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setParameterCount(1); m_pkt.setParameter(0, attr); // Check if the file name contains a path String delName = fname; if ( delName.startsWith("\\") == false) delName = PCShare.makePath(getWorkingDirectory(), fname); // Copy the file name data block to the SMB packet m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString(delName, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB delete file packet m_pkt.ExchangeSMB(this, m_pkt, true); } /** * Get disk information for this remote disk. * * @return Disk information object, or null. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final DiskInfo getDiskInformation() throws java.io.IOException, SMBException { // Check if the NT dialect has been negotiated, or LanMan if ( this.getDialect() != Dialect.NT) { // Create a query disk information SMB packet m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setCommand(PacketType.DiskInformation); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setParameterCount(0); m_pkt.setByteCount(0); // Send/receive the SMB file information packet m_pkt.ExchangeSMB(this, m_pkt, true); // Extract the disk information from the received SMB packet int totunit = m_pkt.getParameter(0); int blkperunit = m_pkt.getParameter(1); int blksize = m_pkt.getParameter(2); int freeblk = m_pkt.getParameter(3); // Create a disk information object return new DiskInfo(getPCShare(), totunit, blkperunit, blksize, freeblk); } else { // Create the transaction request TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryFileSys, null, 0, 2, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(FileInfoLevel.FSInfoQuerySize); // Perform the get file system information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); TransactBuffer respBuf = tpkt.doTransaction(this, reqBuf); // Unpack the response data DiskInfo dinfo = null; if ( respBuf != null && respBuf.hasDataBuffer()) { // Unpack the file system information DataBuffer dataBuf = respBuf.getDataBuffer(); long fsTotalUnit = dataBuf.getLong(); long fsAvailUnit = dataBuf.getLong(); int fsSectorsPerUnit = dataBuf.getInt(); int fsBytesPerSector = dataBuf.getInt(); // Create the disk information details dinfo = new DiskInfo(getPCShare(), fsTotalUnit, fsSectorsPerUnit, fsBytesPerSector, fsAvailUnit); } // Return the disk information return dinfo; } } /** * Get file information for the specified file. * * @param fname File name of the file to return information for. * @see org.alfresco.jlan.smb.FileInfoLevel * @param level Information level required * @return FileInfo if the request was successful, else null. * @exception java.io.IOException If an I/O error occurs. * @exception java.io.FileNotFoundException If the remote file does not exist. * @exception SMBException If an SMB level error occurs */ public final FileInfo getFileInformation(String fname, int level) throws java.io.IOException, java.io.FileNotFoundException, SMBException { // Build the file name/path string String pathName = fname; if ( pathName.startsWith("\\") == false) pathName = PCShare.makePath(getWorkingDirectory(), fname); // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryPath, null, 0, 512, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(level); paramBuf.putInt(0); paramBuf.putString(pathName, isUnicode()); // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); TransactBuffer respBuf = tpkt.doTransaction(this, reqBuf); // Unpack the received file information data FileInfo finfo = null; if ( respBuf != null && respBuf.hasDataBuffer()) { // Unpack the file information DataBuffer buf = respBuf.getDataBuffer(); switch (level) { case FileInfoLevel.PathStandard: finfo = FileInfoPacker.unpackFileInfoStandard("", buf, false); break; case FileInfoLevel.PathQueryEASize: finfo = FileInfoPacker.unpackFileInfoStandard("", buf, true); break; case FileInfoLevel.PathAllEAs: finfo = FileInfoPacker.unpackQueryAllEAs("", buf); break; case FileInfoLevel.PathFileBasicInfo: finfo = FileInfoPacker.unpackQueryBasicInfo("", buf); break; case FileInfoLevel.PathFileStandardInfo: finfo = FileInfoPacker.unpackQueryStandardInfo("", buf); break; case FileInfoLevel.PathFileEAInfo: finfo = FileInfoPacker.unpackQueryEAInfo("", buf); break; case FileInfoLevel.PathFileNameInfo: finfo = FileInfoPacker.unpackQueryNameInfo(buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileAllInfo: finfo = FileInfoPacker.unpackQueryAllInfo(buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileAltNameInfo: finfo = FileInfoPacker.unpackQueryNameInfo(buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileStreamInfo: finfo = FileInfoPacker.unpackQueryStreamInfo("", buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileCompressionInfo: finfo = FileInfoPacker.unpackQueryCompressionInfo("", buf); break; } } // Return the file information return finfo; } /** * Get the disk volume information * * @return VolumeInfo, or null * @exception java.io.IOException If an I/O error occurs * @exception SMBException If an SMB level error occurs */ public final VolumeInfo getVolumeInformation() throws java.io.IOException, SMBException { // Check if the NT dialect has been negotiated, or LanMan VolumeInfo volInfo = null; if ( this.getDialect() != Dialect.NT) { // Build the search request m_pkt.setCommand(PacketType.Search); m_pkt.setUserId(getUserId()); m_pkt.setTreeId(getTreeId()); // Initialize the search SMB packet m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setParameterCount(2); m_pkt.setParameter(0, 1); // number of directory entries to return m_pkt.setParameter(1, FileAttribute.Volume); // Pack the search string m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString("", false); // Append a null resume key, to indicate the start of a new search m_pkt.packByte(DataType.VariableBlock); m_pkt.packWord(0); m_pkt.setByteCount(); // Send/receive the search SMB packet m_pkt.ExchangeSMB(this, m_pkt, true); // Unpack the volume label m_pkt.resetBytePointer(); m_pkt.skipBytes(33); // data type byte + length word + offset to file name/volume label String label = m_pkt.unpackString(m_pkt.isUnicode()); // Create the volume information object volInfo = new VolumeInfo(label); } else { // Create the transaction request TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryFileSys, null, 0, 2, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(FileInfoLevel.FSInfoQueryVolume); // Perform the get file system information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); TransactBuffer respBuf = tpkt.doTransaction(this, reqBuf); // Unpack the volume information if ( respBuf != null && respBuf.hasDataBuffer()) { // Get the data buffer DataBuffer dataBuf = respBuf.getDataBuffer(); // Get the volume information long createTime = dataBuf.getLong(); int serNo = dataBuf.getInt(); int nameLen = dataBuf.getInt(); if ( respBuf.isUnicode()) nameLen = nameLen / 2; dataBuf.skipBytes(2); String label = dataBuf.getString(nameLen, respBuf.isUnicode()); // Create the volume information volInfo = new VolumeInfo(label, serNo, NTTime.toSMBDate(createTime)); } } // Return the volume information return volInfo; } /** * Check if the specified file name is a directory. * * @param dir Directory name string. If the directory name does not have a leading '\' the * current working directory for this session will be preprended to the string. * @return true if the specified file name is a directory, else false. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final boolean isDirectory(String dir) throws java.io.IOException, SMBException { // Allocate an SMB packet for the check directory request m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setCommand(PacketType.CheckDirectory); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setParameterCount(0); // Build the remote directory tree relative path String pathName = dir; if ( pathName.startsWith("\\") == false) pathName = PCShare.makePath(getWorkingDirectory(), dir); // Pack the directory name m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString(pathName, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB check directory packet m_pkt.ExchangeSMB(this, m_pkt); // Check if a valid response was received, indicates the path is a directory return m_pkt.isValidResponse(); } /** * Open a file on the remote file server. * * @param fname Remote file name string. * @param flags File open option flags. * @return SMBFile for the opened file, else null. * @exception java.io.IOException If an I/O error occurs * @exception SMBException If an SMB level error occurs */ public final SMBFile OpenFile(String fname, int flags) throws java.io.IOException, SMBException { // Check if the path is a valid file path if ( isValidFilePath(fname) == false) throw new SMBException(SMBStatus.NTErr, SMBStatus.NTInvalidParameter); // Build the file name details String fileName = fname; if ( fileName.startsWith("\\") == false) fileName = PCShare.makePath(getWorkingDirectory(), fname); // Pre-NT dialect open file if ( getDialect() != Dialect.NT || isUnicode() == false) { // Initialize the SMB request to open an existing file m_pkt.setCommand(PacketType.OpenAndX); m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); // Set the parameter words m_pkt.setParameterCount(15); m_pkt.setAndXCommand(0xFF); // no secondary command m_pkt.setParameter(1, 0); // offset to next command m_pkt.setParameter(2, 0x01); // return additional information m_pkt.setParameter(3, flags); m_pkt.setParameter(4, 0); // normal files only for now m_pkt.setParameter(5, 0); // file attributes m_pkt.setParameter(6, 0); // creation time m_pkt.setParameter(7, 0); // creation date // Default open mode is 'open if file exists' int openMode = FileAction.OpenIfExists; if ( AccessMode.getAccessMode(flags) == AccessMode.WriteOnly) { // Truncate the file if it exists, create file if it does not exist openMode = FileAction.CreateNotExist + FileAction.TruncateExisting; } else if ( AccessMode.getAccessMode(flags) == AccessMode.ReadWrite) { // Open the file if it exists, create the file if it does not exist openMode = FileAction.CreateNotExist + FileAction.OpenIfExists; } m_pkt.setParameter(8, openMode); m_pkt.setParameter(9, 0); // default allocation on create/truncate (long) m_pkt.setParameter(10, 0); // ... high word m_pkt.setParameter(11, 0); m_pkt.setParameter(12, 0); m_pkt.setParameter(13, 0); m_pkt.setParameter(14, 0); // Pack the file name string m_pkt.resetBytePointer(); m_pkt.packString(fileName, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB file open packet m_pkt.ExchangeSMB(this, m_pkt, true); // Extract the file information from the received SMB packet int fid = m_pkt.getParameter(2); int attr = m_pkt.getParameter(3); int fsiz = (m_pkt.getParameter(7) << 16) + m_pkt.getParameter(6); // Create a file information object FileInfo finfo = new FileInfo(fname, fsiz, attr); // Create an SMB file object return new CIFSFile(this, finfo, fid); } else { // Default open mode is 'open if file exists' int openMode = FileAction.NTOpen; int accessMode = AccessMode.NTRead; if ( AccessMode.getAccessMode(flags) == AccessMode.WriteOnly) { // Truncate the file if it exists, create file if it does not exist openMode = FileAction.NTOverwriteIf; accessMode = AccessMode.NTWrite; } else if ( AccessMode.getAccessMode(flags) == AccessMode.ReadWrite) { // Open the file if it exists, create the file if it does not exist openMode = FileAction.NTOpenIf; accessMode = AccessMode.NTReadWrite; } // Open the remote file return NTCreate(fileName, accessMode, FileAttribute.NTNormal, SharingMode.READWRITEDELETE, openMode, 0, WinNT.CreateFile); } } /** * Rename a file, or set of files, on the remote file server. * * @param curnam Current file name string, may contain wildcards. If the path does not start * with a '\' the current working directory string will be preprended. * @param newnam New file name. * @param attr Search attributes, to determine which file(s) to rename. * @see org.alfresco.jlan.server.filesys.FileAttribute * @return true if the file rename request was successful, else false. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final boolean RenameFile(String curnam, String newnam, int attr) throws java.io.IOException, SMBException { // Create an SMB rename packet m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setCommand(PacketType.RenameFile); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setParameterCount(1); m_pkt.setParameter(0, attr); // Add the current working directory path to the file names if they do not contain a path String fromName = curnam; if ( fromName.startsWith("\\") == false) fromName = PCShare.makePath(getWorkingDirectory(), curnam); String toName = newnam; if ( toName.startsWith("\\") == false) toName = PCShare.makePath(getWorkingDirectory(), newnam); // Pack the current and new file names m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString(fromName, m_pkt.isUnicode()); m_pkt.packByte(DataType.ASCII); m_pkt.packString(toName, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB rename file(s) packet m_pkt.ExchangeSMB(this, m_pkt, true); // Check if we got a valid response if ( m_pkt.isValidResponse()) return true; // Invalid rename request return false; } /** * Set file information for the specified file. * * @param fname File name of the file to set information for. * @param finfo File information containing the new values. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final void setFileInformation(String fname, FileInfo finfo) throws java.io.IOException, SMBException { // Create an SMB set file information packet m_pkt.setCommand(PacketType.SetFileAttributes); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); // Set the call parameters m_pkt.setParameterCount(8); m_pkt.setParameter(0, finfo.getFileAttributes()); m_pkt.setParameter(1, finfo.getModifyDateTime().asSMBTime()); m_pkt.setParameter(2, finfo.getModifyDateTime().asSMBDate()); for (int i = 3; i < 8; i++) m_pkt.setParameter(i, 0); // Build the full path string String fileName = fname; if ( fname.startsWith("\\") == false) fileName = PCShare.makePath(getWorkingDirectory(), fname); // Pack the file name m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString(fileName, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB set file information packet m_pkt.ExchangeSMB(this, m_pkt, true); } /** * Set file information for the specified file, using the file id * * @param file File to set information for. * @param finfo File information containing the new values. * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final void setFileInformation(SMBFile file, FileInfo finfo) throws java.io.IOException, SMBException { // Create an SMB set file information packet m_pkt.setCommand(PacketType.SetInformation2); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); // Set the call parameters m_pkt.setParameterCount(7); m_pkt.setParameter(0, file.getFileId()); // Pack the new creation date/time, if available if ( finfo.hasCreationDateTime()) { m_pkt.setParameter(1, finfo.getCreationDateTime().asSMBTime()); m_pkt.setParameter(2, finfo.getCreationDateTime().asSMBDate()); } else { m_pkt.setParameter(1, 0); m_pkt.setParameter(2, 0); } // Pack the new access date/time, if available if ( finfo.hasAccessDateTime()) { m_pkt.setParameter(3, finfo.getAccessDateTime().asSMBTime()); m_pkt.setParameter(4, finfo.getAccessDateTime().asSMBDate()); } else { m_pkt.setParameter(3, 0); m_pkt.setParameter(4, 0); } // Pack the new modify date/time, if available if ( finfo.hasModifyDateTime()) { m_pkt.setParameter(5, finfo.getModifyDateTime().asSMBTime()); m_pkt.setParameter(6, finfo.getModifyDateTime().asSMBDate()); } else { m_pkt.setParameter(5, 0); m_pkt.setParameter(6, 0); } // Clear the byte count m_pkt.setByteCount(0); // Send/receive the SMB set file information packet m_pkt.ExchangeSMB(this, m_pkt, true); } /** * Set file attributes for the specified file, using the file name * * @param fname File name of the file to set information for. * @param attrib File attributes mask * @see org.alfresco.jlan.server.filesys.FileAttribute * @exception java.io.IOException If an I/O error occurs. * @exception SMBException If an SMB level error occurs */ public final void setFileAttributes(String fname, int attrib) throws java.io.IOException, SMBException { // Create an SMB set file information packet m_pkt.setCommand(PacketType.SetFileAttributes); m_pkt.setUserId(this.getUserId()); m_pkt.setTreeId(this.getTreeId()); m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); // Set the call parameters m_pkt.setParameterCount(8); m_pkt.setParameter(0, attrib); m_pkt.setParameter(1, 0); m_pkt.setParameter(2, 0); for (int i = 3; i < 8; i++) m_pkt.setParameter(i, 0); // Build the full path string String fileName = fname; if ( fname.startsWith("\\") == false) fileName = PCShare.makePath(getWorkingDirectory(), fname); // Pack the file name m_pkt.resetBytePointer(); m_pkt.packByte(DataType.ASCII); m_pkt.packString(fileName, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the SMB set file information packet m_pkt.ExchangeSMB(this, m_pkt, true); } /** * Start a search of the specified directory returning information for each file/directory * found. * * @param dir Directory to start searching. If the directory string does not start with a '\' * then the directory name is prepended with the current working directory. * @param attr Search attributes, to determine the types of files/directories returned. @see * org.alfresco.jlan.server.filesys.FileAttribute * @param level Information level required * @return SMBSearchContext for this search, else null * @exception java.io.IOException If an I/O error occurs * @exception SMBException If an SMB level error occurs */ public final SearchContext StartSearch(String dir, int attr, int level) throws java.io.IOException, SMBException { // Create a new SMB search context TransSearchContext srch = new TransSearchContext(this); if ( srch == null) return null; // Start the search and return the search context srch.StartSearch(dir, attr, level); return srch; } /** * Perform an NTCreateAndX SMB to create/open a file or directory * * @param name File/directory name * @param access Desired access mode. * @see org.alfresco.jlan.server.filesys.AccessMode * @param attrib Required file attributes. * @see org.alfresco.jlan.server.filesys.FileAttribute * @param sharing Shared access mode * @param exists Action to take if file/directory exists. * @see org.alfresco.jlan.server.filesys.FileAction * @param initSize Initial file allocation size, in bytes * @param createOpt Create file options * @return CIFSFile * @exception IOException * @exception SMBException If an SMB level error occurs */ public final CIFSFile NTCreate(String name, int access, int attrib, int sharing, int exists, long initSize, int createOpt) throws IOException, SMBException { // Call the main NTCreate method return NTCreateInternal(name, 0, access, attrib, sharing, exists, initSize, createOpt, true); } /** * Perform an NTCreateAndX SMB to create/open a file with an oplock * * @param name File/directory name * @param oplockFlags int * @param access Desired access mode. * @see org.alfresco.jlan.server.filesys.AccessMode * @param attrib Required file attributes. * @see org.alfresco.jlan.server.filesys.FileAttribute * @param sharing Shared access mode * @param exists Action to take if file/directory exists. * @see org.alfresco.jlan.server.filesys.FileAction * @param initSize Initial file allocation size, in bytes * @param createOpt Create file options * @return CIFSFile * @exception IOException * @exception SMBException If an SMB level error occurs */ public final CIFSFile NTCreateWithOplock(String name, int oplockFlags, OplockInterface oplockIface, int access, int attrib, int sharing, int exists, long initSize, int createOpt) throws IOException, SMBException { // Call the main NTCreate method CIFSFile cifsFile = NTCreateInternal(name, oplockFlags, access, attrib, sharing, exists, initSize, createOpt, true); if ( cifsFile != null && cifsFile.getOplockType() != OpLock.TypeNone) { // Set the oplock interface cifsFile.setOplockInterface( oplockIface); // Add the file to the list of oplocked files, need to access the file to call // the oplock interface if an oplock break is received asynchronously from the server if ( m_oplockFiles == null) m_oplockFiles = new HashMap(); m_oplockFiles.put( new Integer( cifsFile.getFileId()), cifsFile); } // Return the file return cifsFile; } /** * Perform an NT query security descriptor transaction for the specified file or directory * * @param fid File identifier, via SMBFile.getFileId() of an open file. * @param flags Security descriptor elements to return (Owner, Group, SACL, DACL). * @see org.alfresco.jlan.smb.nt.SecurityDescriptor * @return SecurityDescriptor * @exception IOException * @exception SMBException If an SMB level error occurs */ public final SecurityDescriptor NTQuerySecurityDescriptor(int fid, int flags) throws IOException, SMBException { // Check if we have negotiated NT dialect if ( getDialect() != Dialect.NT) throw new SMBException(SMBStatus.NetErr, SMBStatus.NETUnsupported); // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.NTTransQuerySecurityDesc, null, 0, 8, 0); // Pack the parameter block for the request DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(fid); paramBuf.putShort(0); paramBuf.putInt(flags); // Perform the query security descriptor transaction NTTransPacket ntPkt = new NTTransPacket(); TransactBuffer respBuf = ntPkt.doTransaction(this, reqBuf); // Check if a security descriptor has been returned SecurityDescriptor secDesc = null; if ( respBuf != null && respBuf.hasDataBuffer()) { // Get the returned data DataBuffer dataBuf = respBuf.getDataBuffer(); try { secDesc = new SecurityDescriptor(); secDesc.loadDescriptor(dataBuf.getBuffer(), dataBuf.getOffset()); } catch (LoadException ex) { secDesc = null; } } // Return the security descriptor return secDesc; } /** * Set the security descriptor for the specified file/directory * * @param fid File identifier, via SMBFile.getFileId() of an open file. * @param secdesc Security descriptor * @param flags Fields to set (Owner, Group, SACL, DACL). * @see org.alfresco.jlan.smb.nt.SecurityDescriptor * @exception IOException If a network error occurs * @exception SMBException If an SMB level error occurs * @exception SaveException If the security descriptor cannot be stored */ public final void NTSetSecurityDescriptor(int fid, SecurityDescriptor secdesc, int flags) throws IOException, SMBException, SaveException { // Check if we have negotiated NT dialect if ( getDialect() != Dialect.NT) throw new SMBException(SMBStatus.NetErr, SMBStatus.NETUnsupported); // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.NTTransSetSecurityDesc, null, 0, 8, 65000); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(fid); paramBuf.putShort(0); paramBuf.putInt(flags); // Pack the data block DataBuffer dataBuf = reqBuf.getDataBuffer(); int len = secdesc.saveDescriptor(dataBuf.getBuffer(), 0); dataBuf.setLength(len); // Perform the set security descriptor transaction NTTransPacket ntPkt = new NTTransPacket(); ntPkt.doTransaction(this, reqBuf); } /** * Add a change notification filter for the specified directory * * @param fid File id, from SMBFile.getFileId() of an open directory. The directory should be * opened using the NTCreate() method. * @param filter Directory watch filter flags. @see org.alfresco.jlan.client.nt.NotifyChange. * @param watchTree true to watch sub-directories, false to watch the specified directory only * @param handler DirectoryWatcher implementation. @see * org.alfresco.jlan.client.smb.DirectoryWatcher * @param autoResub true to automatically resubmit the notification filter after an event is * received * @return AsynchRequest * @exception IOException * @exception SMBException If an SMB level error occurs */ public final AsynchRequest NTNotifyChange(int fid, int filter, boolean watchTree, DirectoryWatcher handler, boolean autoResub) throws IOException, SMBException { // Create an asynchronous request to hold the notify details NotifyChangeAsynchRequest areq = new NotifyChangeAsynchRequest(-1, fid, filter, watchTree, handler); areq.setAutoReset(autoResub); // Call the main notify change method to set the notify request return NTNotifyChange(areq); } /** * Add a change notification filter for the specified directory * * @param areq AsynchRequest * @return AsynchRequest * @exception IOException * @exception SMBException If an SMB level error occurs */ public final AsynchRequest NTNotifyChange(AsynchRequest areq) throws IOException, SMBException { // Check if we have negotiated NT dialect if ( getDialect() != Dialect.NT) throw new SMBException(SMBStatus.NetErr, SMBStatus.NETUnsupported); // Make sure the request is a notify change request if ( areq instanceof NotifyChangeAsynchRequest == false) throw new IOException("Invalid asynchronous request class, " + areq.getClass().getName()); // Get the notify change asynchronous request NotifyChangeAsynchRequest nreq = (NotifyChangeAsynchRequest) areq; // Build the NT notify change transaction SMB packet NTTransPacket tpkt = new NTTransPacket(); tpkt.setUserId(getUserId()); tpkt.setTreeId(getTreeId()); tpkt.setFlags(getDefaultFlags()); tpkt.setFlags2(getDefaultFlags2()); // Set the multiplex id to identify this notify request int mid = getNextMultiplexId(); tpkt.setMultiplexId(mid); // Save the request id and clear the completed status of the request nreq.setId(mid); nreq.setCompleted(false); // Initialize the NT transaction packet tpkt.InitializeNTTransact(PacketType.NTTransNotifyChange, null, 0, null, 0, 4, 32, 0); tpkt.resetSetupPointer(); tpkt.packInt(nreq.getFilter()); tpkt.packWord(nreq.getFileId()); tpkt.packByte(nreq.hasWatchTree() ? 1 : 0); tpkt.packByte(0); tpkt.setByteCount(0); // Send the notify change transaction // // Note: There is no response until the notification triggers tpkt.SendSMB(this); // Add the request to the pending list and return the request addAsynchronousRequest(nreq); return nreq; } /** * Cancel an outstanding request. Used to cancel change notifications. * * @param req AsynchRequest * @exception IOException * @exception SMBException If an SMB level error occurs */ public final void NTCancel(AsynchRequest areq) throws IOException, SMBException { // Check if we have negotiated NT dialect if ( getDialect() != Dialect.NT) throw new SMBException(SMBStatus.NetErr, SMBStatus.NETUnsupported); // Check if the request has auto-resubmit enabled, if so then disable the resubmit if ( areq.hasAutoReset()) areq.setAutoReset(false); // Check if the request has already completed, if so then there is no need to cancel it if ( areq.hasCompleted()) return; // Check if there is any data available for this network session, the request we are about // to // cancel may have just completed if ( getSession().hasData()) { // Clear out the recieve buffer pingServer(); // Check if the request has now completed if ( areq.hasCompleted()) return; } // Build the NTCancel SMB packet m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setCommand(PacketType.NTCancel); m_pkt.setUserId(getUserId()); m_pkt.setTreeId(getTreeId()); m_pkt.setMultiplexId(areq.getId()); m_pkt.setParameterCount(0); m_pkt.setByteCount(0); // Send/receive the NT cancel request m_pkt.ExchangeSMB(this, m_pkt, false); // Check the return status boolean isValid = m_pkt.isValidResponse(); if ( isValid == true) return; else if ( m_pkt.isLongErrorCode() && m_pkt.getLongErrorCode() == SMBStatus.NTCancelled) return; else { // Throw the SMB exception if ( m_pkt.hasLongErrorCode()) throw new SMBException(SMBStatus.NTErr, m_pkt.getLongErrorCode()); else throw new SMBException(m_pkt.getErrorClass(), m_pkt.getErrorCode()); } } /** * NT I/O control * * @param ctrlCode int * @param fid int * @param fsctl boolean * @param data byte[] * @param dlen int * @param filter int * @return DataBuffer * @exception IOException * @exception SMBException If an SMB level error occurs */ public final DataBuffer NTIOCtl(int ctrlCode, int fid, boolean fsctl, byte[] data, int dlen, int filter) throws IOException, SMBException { // Check if we have negotiated NT dialect if ( getDialect() != Dialect.NT) throw new SMBException(SMBStatus.NetErr, SMBStatus.NETUnsupported); // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.NTTransIOCtl, 8, 0, data, 0, dlen); // Pack the setup block DataBuffer setupBuf = reqBuf.getSetupBuffer(); setupBuf.putInt(ctrlCode); setupBuf.putShort(fid); setupBuf.putByte(fsctl ? 1 : 0); setupBuf.putByte(filter); // Perform the I/O control transaction NTTransPacket ntPkt = new NTTransPacket(); TransactBuffer respBuf = ntPkt.doTransaction(this, reqBuf); // Check if there is any return data if ( respBuf != null) return respBuf.getDataBuffer(); return null; } /** * Get file information for the specified open file/directory, returning the requested * information level * * @param fid File id for the file or directory, from SMBFile.getFileId(). * @param level Information level. @see org.alfresco.jlan.smb.FileInfoLevel * @return FileInfo * @exception IOException * @exception SMBException If an SMB level error occurs */ public final FileInfo NTGetFileInformation(int fid, int level) throws IOException, SMBException { // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryFile, null, 0, 4, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(fid); paramBuf.putShort(level); // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); TransactBuffer respBuf = tpkt.doTransaction(this, reqBuf); // Unpack the received file information data FileInfo finfo = null; if ( respBuf != null && respBuf.hasDataBuffer()) { // Unpack the file information DataBuffer buf = respBuf.getDataBuffer(); switch (level) { case FileInfoLevel.PathStandard: finfo = FileInfoPacker.unpackFileInfoStandard("", buf, false); break; case FileInfoLevel.PathQueryEASize: finfo = FileInfoPacker.unpackFileInfoStandard("", buf, true); break; case FileInfoLevel.PathAllEAs: break; case FileInfoLevel.PathFileBasicInfo: finfo = FileInfoPacker.unpackQueryBasicInfo("", buf); break; case FileInfoLevel.PathFileStandardInfo: finfo = FileInfoPacker.unpackQueryStandardInfo("", buf); break; case FileInfoLevel.PathFileEAInfo: finfo = FileInfoPacker.unpackQueryEAInfo("", buf); break; case FileInfoLevel.PathFileNameInfo: finfo = FileInfoPacker.unpackQueryNameInfo(buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileAllInfo: finfo = FileInfoPacker.unpackQueryAllInfo(buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileAltNameInfo: finfo = FileInfoPacker.unpackQueryNameInfo(buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileStreamInfo: finfo = FileInfoPacker.unpackQueryStreamInfo("", buf, respBuf.isUnicode()); break; case FileInfoLevel.PathFileCompressionInfo: finfo = FileInfoPacker.unpackQueryCompressionInfo("", buf); break; } } // If the file information is valid, set the file id so the file information can be used // to set information if ( finfo != null) finfo.setFileId(fid); // Return the file information return finfo; } /** * Get file information for the specified open file/directory, returning the requested * information level * * @param fid File id for the file or directory, from SMBFile.getFileId(). * @param level Information level. @see org.alfresco.jlan.smb.FileInfoLevel * @return TransactBuffer * @exception IOException * @exception SMBException If an SMB level error occurs */ public final TransactBuffer NTGetFileInformationRaw(int fid, int level) throws IOException, SMBException { // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryFile, null, 0, 4, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(fid); paramBuf.putShort(level); // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); return tpkt.doTransaction(this, reqBuf); } /** * Set file information that allows setting different information levels * * @param finfo FileInfo * @param level Information level. @see org.alfresco.jlan.smb.FileInfoLevel * @exception IOException * @exception SMBException If an SMB level error occurs */ public final void NTSetFileInformation(FileInfo finfo, int level) throws IOException, SMBException { // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2SetFile, null, 0, 6, 65000); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(finfo.getFileId()); paramBuf.putShort(level); paramBuf.putShort(0); // Pack the file information into the data buffer DataBuffer dataBuf = reqBuf.getDataBuffer(); switch (level) { case FileInfoLevel.SetStandard: FileInfoPacker.packFileInfoStandard(finfo, dataBuf, false); break; case FileInfoLevel.SetQueryEASize: FileInfoPacker.packFileInfoStandard(finfo, dataBuf, true); break; case FileInfoLevel.SetBasicInfo: FileInfoPacker.packFileBasicInfo(finfo, dataBuf); break; case FileInfoLevel.SetDispositionInfo: break; case FileInfoLevel.SetAllocationInfo: break; case FileInfoLevel.SetEndOfFileInfo: break; } ; // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); tpkt.doTransaction(this, reqBuf); } /** * Set the delete on close flag for an open file * * @param fid File id for the file or directory, from SMBFile.getFileId(). * @param delFlag true to delete the file on close, or false to clear a previous delete on close * request * @exception IOException * @exception SMBException If an SMB level error occurs */ public final void NTSetDeleteOnClose(int fid, boolean delFlag) throws IOException, SMBException { // Create the data block for the set file disposition info level (0x102) byte[] dblock = new byte[4]; dblock[0] = delFlag == true ? (byte) 1 : (byte) 0; // Set the delete on close setting for the open file NTSetFileInformationRaw(fid, dblock, 2, 0x102); } /** * Set the end of file position for the open file * * @param fid File id for the file or directory, from SMBFile.getFileId(). * @param pos New end of file position * @exception IOException * @exception SMBException If an SMB level error occurs */ public final void NTSetEndOfFile(int fid, long pos) throws IOException, SMBException { // Create the data block for the set end of file info level (0x104) byte[] dblock = new byte[8]; DataPacker.putIntelLong(pos, dblock, 0); // Set the end of file position for the open file NTSetFileInformationRaw(fid, dblock, 8, 0x104); } /** * Set the file allocation size for the open file * * @param fid File id for the file or directory, from SMBFile.getFileId(). * @param alloc New file allocation size * @exception IOException * @exception SMBException If an SMB level error occurs */ public final void NTSetFileAllocation(int fid, long alloc) throws IOException, SMBException { // Create the data block for the set file allocation info level (0x103) byte[] dblock = new byte[8]; DataPacker.putIntelLong(alloc, dblock, 0); // Set the file allocation for the open file NTSetFileInformationRaw(fid, dblock, 8, 0x103); } /** * Set file information that allows setting different information levels * * @param fid File id for the file or directory, from SMBFile.getFileId(). * @param data Raw file information data block. * @param dlen Raw data block length. * @param level File information level. @see org.alfresco.jlan.smb.FileInfoLevel * @exception IOException * @exception SMBException If an SMB level error occurs */ private final void NTSetFileInformationRaw(int fid, byte[] data, int dlen, int level) throws IOException, SMBException { // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2SetFile, 0, 6, data, 0, dlen); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(fid); paramBuf.putShort(level); paramBuf.putShort(0); // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); tpkt.doTransaction(this, reqBuf); } /** * Get the device information * * @return DeviceInfo @see org.alfresco.jlan.client.info.DeviceInfo * @exception IOException * @exception SMBException */ public final DeviceInfo NTGetDeviceInfo() throws IOException, SMBException { // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryFileSys, null, 0, 2, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(FileInfoLevel.FSInfoQueryDevice); // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); TransactBuffer respBuf = tpkt.doTransaction(this, reqBuf); // Unpack the received device information data DeviceInfo devInfo = null; if ( respBuf != null && respBuf.hasDataBuffer()) { // Unpack the device information DataBuffer buf = respBuf.getDataBuffer(); int typ = buf.getInt(); int chr = buf.getInt(); // Return the device information devInfo = new DeviceInfo(typ, chr); } // Return the device information return devInfo; } /** * Get the device attributes information * * @return DeviceAttributesInfo * @exception IOException * @exception SMBException */ public final DeviceAttributesInfo NTGetDeviceAttributes() throws IOException, SMBException { // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryFileSys, null, 0, 2, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(FileInfoLevel.FSInfoQueryAttribute); // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); TransactBuffer respBuf = tpkt.doTransaction(this, reqBuf); // Unpack the received device attribute information data DeviceAttributesInfo attrInfo = null; if ( respBuf != null && respBuf.hasDataBuffer()) { // Unpack the device attribute information DataBuffer buf = respBuf.getDataBuffer(); int attr = buf.getInt(); int maxLen = buf.getInt(); int lblLen = buf.getInt(); if ( respBuf.isUnicode()) lblLen = lblLen / 2; String label = buf.getString(lblLen, respBuf.isUnicode()); // Return the device attributes attrInfo = new DeviceAttributesInfo(attr, maxLen, label); } // Return the device attribute information return attrInfo; } /** * Get file information for the specified file. * * @param fname File name of the file to return information for. * @param level Information level required. @see org.alfresco.jlan.smb.FileInfoLevel * @return TransactBuffer * @exception java.io.IOException If an I/O error occurs. * @exception java.io.FileNotFoundException If the remote file does not exist. * @exception SMBException If an SMB level error occurs */ public final TransactBuffer getFileInformationRaw(String fname, int level) throws java.io.IOException, java.io.FileNotFoundException, SMBException { // Build the file name/path string String pathName = fname; if ( pathName.startsWith("\\") == false) pathName = PCShare.makePath(getWorkingDirectory(), fname); // Create the request transaction buffer TransactBuffer reqBuf = new TransactBuffer(PacketType.Trans2QueryPath, null, 0, 512, 0); // Pack the parameter block DataBuffer paramBuf = reqBuf.getParameterBuffer(); paramBuf.putShort(level); paramBuf.putInt(0); paramBuf.putString(pathName, isUnicode()); // Perform the get file information transaction TransPacket tpkt = new TransPacket(m_pkt.getBuffer()); return tpkt.doTransaction(this, reqBuf); } /** * Return the details for a symlink file/folder * * @param linkPath String * @return SymLink * @exception Exception */ public final SymLink getSymLinkDetails(String linkPath) throws Exception { // Open the symlink file CIFSFile linkFile = NTCreateInternal(linkPath, 0, AccessMode.NTRead + AccessMode.NTReadControl + AccessMode.NTReadAttrib + AccessMode.NTReadEA, FileAttribute.NTNormal, SharingMode.READWRITE, FileAction.NTOpen, 0, WinNT.CreateReparsePoint, false); SymLink symLink = null; Exception retError = null; try { // Make sure the file is a reparse point if ( linkFile.isReparsePoint()) { // Get the symlink details int ioctlCode = NTIOCtl.makeControlCode(NTIOCtl.DeviceFileSystem, NTIOCtl.FsCtlGetReparsePoint, NTIOCtl.MethodBuffered, NTIOCtl.AccessAny); DataBuffer linkBuf = NTIOCtl(ioctlCode, linkFile.getFileId(), true, null, 0, 0); // Parse the returned structure symLink = new SymLink(linkBuf); } else { // Return an exception retError = new IOException("Not a reparse point, " + linkPath); } } catch (Exception ex) { // Save the error retError = ex; } finally { // Close the link file try { linkFile.Close(); } catch (Exception ex) { } } // Check if there is an error to return if ( retError != null) throw retError; // Return the symlink details return symLink; } /** * Process incoming data checking for asynchronous response packets from the server * * @param waitTime Receive timeout in milliseconds, zero for no timeout or -1 to not wait for * data * @exception IOException * @exception SMBException */ public final void checkForAsynchReceive(int waitTime) throws IOException, SMBException { // Check if there is any data in the socket receive buffer, if the caller does not want // to wait for a packet then return immediately if ( waitTime == -1 && getSession().hasData() == false) { // Check if we need to send an echo packet to the server to keep the SMB session alive if ( (m_pkt.getLastPacketSendTime() + SessionKeepAlive) < System.currentTimeMillis()) pingServer(); return; } // Wait for an asynchronous response from the server m_pkt.ReceiveAsynchSMB(this, waitTime == -1 ? 0 : waitTime); // Check if we need to send an echo packet to the server to keep the SMB session alive. // // The asynchronous receive will usually result in the request being reset on the server, if // not then we may need to send an echo request. if ( (m_pkt.getLastPacketSendTime() + SessionKeepAlive) < System.currentTimeMillis()) pingServer(); } /** * Refresh the file information for an open file * * @param smbFile SMBFile */ public void refreshFileInformation( SMBFile smbFile) throws IOException, SMBException { } /** * Process an asynchronous response packet * * @param pkt SMBPacket */ protected void processAsynchResponse(SMBPacket pkt) { // Check for a locking request from the server, an oplock break if ( pkt.isRequest() == true && pkt.getCommand() == PacketType.LockingAndX) { // Unpack the file id and flags int fileId = pkt.getParameter( 2); int flags = pkt.getParameter( 3); // Check for an oplock break if ( m_oplockFiles != null && ( flags & LockingAndX.OplockBreak) != 0) { try { // Find the oplocked file CIFSFile cifsFile = m_oplockFiles.get( new Integer( fileId)); int breakToOpLock = OpLock.TypeNone; if ( cifsFile != null) { // Check if the file has an oplock callback interface if ( cifsFile.getOplockInterface() != null) { // Call the oplock interface breakToOpLock = cifsFile.getOplockInterface().oplockBreak( cifsFile); } else { // Flush any pending data on the file cifsFile.Flush(); } } // Check if an oplock break response should be sent if ( cifsFile.getOplockInterface().sendAutomaticBreakResponse() == true) { // Build an oplock break response SMBPacket respPkt = new SMBPacket( 128); respPkt.setCommand(PacketType.LockingAndX); respPkt.setUserId(this.getUserId()); respPkt.setTreeId(this.getTreeId()); respPkt.setFlags(getDefaultFlags() + SMBPacket.FLG_RESPONSE); respPkt.setFlags2(getDefaultFlags2()); respPkt.setParameterCount(8); respPkt.setAndXCommand( PacketType.NoChainedCommand); respPkt.setParameter(1, 0); // AndX offset respPkt.setParameter(2, fileId); // Break the oplock, or break to a level II shared oplock if ( breakToOpLock == OpLock.TypeLevelII) respPkt.setParameter(3, LockingAndX.OplockBreak + LockingAndX.Level2OpLock); else respPkt.setParameter(3, LockingAndX.OplockBreak); respPkt.setParameterLong(4, 0); // timeout respPkt.setParameter(6, 0); // number of unlocks respPkt.setParameter(7, 0); // number of locks respPkt.setByteCount( 0); // Send the oplock break to the server // // Note: The response flag must be set, and we do not expect a response from the server respPkt.SendSMB( this); // Set the new oplock type on the file cifsFile.setOplockType( breakToOpLock); cifsFile.setOplockInterface( null); } } catch (Exception ex) { } } } else { // Check if there are any pending asynchronous requests queued if ( m_asynchRequests == null || m_asynchRequests.size() == 0) return; // Find the matching asynchronous request and remove from the pending list AsynchRequest areq = removeAsynchronousRequest(pkt.getMultiplexId()); if ( areq == null) return; // Mark the asynchronous request as completed areq.setCompleted(true); // Pass the packet to the asynchronous request for processing areq.processResponse(this, pkt); // Check if the request should be automatically resubmitted if ( areq.hasAutoReset()) { // Resubmit the request if ( areq.resubmitRequest(this, null) == true) addAsynchronousRequest(areq); } } } /** * Add an asynchronous request to the list of pending requests * * @param req AsynchRequest */ protected final void addAsynchronousRequest(AsynchRequest req) { // Check if the asynchronous request list has been allocated if ( m_asynchRequests == null) m_asynchRequests = new ArrayList(); // Add the request to the list m_asynchRequests.add(req); } /** * Remove an asynchronous request from the pending list * * @param id int * @return AsynchRequest */ protected final AsynchRequest removeAsynchronousRequest(int id) { // Check if the list is valid if ( m_asynchRequests == null) return null; // Find the request AsynchRequest areq = null; int idx = 0; while (idx < m_asynchRequests.size() && areq == null) { // Get the current request and check if it is the required request AsynchRequest curReq = m_asynchRequests.get(idx); if ( curReq.getId() == id) areq = curReq; else idx++; } // Remove the request from the list if ( areq != null) m_asynchRequests.remove(areq); // Return the removed request return areq; } /** * Remove an asynchronous request from the pending list * * @param req AsynchRequest * @return AsynchRequest */ protected final AsynchRequest removeAsynchronousRequest(AsynchRequest req) { // Check if the list is valid if ( m_asynchRequests == null) return null; // Remove the request from the list m_asynchRequests.remove(req); return req; } /** * Perform an NTCreateAndX SMB to create/open a file or directory * * @param name File/directory name * @param createFlags int * @param access Desired access mode. * @see org.alfresco.jlan.server.filesys.AccessMode * @param attrib Required file attributes. * @see org.alfresco.jlan.server.filesys.FileAttribute * @param sharing Shared access mode * @param exists Action to take if file/directory exists. * @see org.alfresco.jlan.server.filesys.FileAction * @param initSize Initial file allocation size, in bytes * @param createOpt Create file options * @param throwErr Throw errors from the CIFS packet exchange * @return CIFSFile * @exception IOException * @exception SMBException If an SMB level error occurs */ protected final CIFSFile NTCreateInternal(String name, int createFlags, int access, int attrib, int sharing, int exists, long initSize, int createOpt, boolean throwErr) throws IOException, SMBException { // Check if we have negotiated NT dialect if ( getDialect() != Dialect.NT) throw new SMBException(SMBStatus.NetErr, SMBStatus.NETUnsupported); // Build the NTCreateAndX SMB packet m_pkt.setFlags(getDefaultFlags()); m_pkt.setFlags2(getDefaultFlags2()); m_pkt.setCommand(PacketType.NTCreateAndX); m_pkt.setUserId(getUserId()); m_pkt.setTreeId(getTreeId()); m_pkt.setParameterCount(24); m_pkt.resetParameterPointer(); m_pkt.packByte(0xFF); // no chained command m_pkt.packByte(0); // reserved m_pkt.packWord(0); // AndX offset m_pkt.packByte(0); // reserved m_pkt.packWord((name.length() * 2) + 2); // name length in bytes, inc null m_pkt.packInt(createFlags); // oplocks/extended response m_pkt.packInt(0); // root FID m_pkt.packInt(access); // desired access mode m_pkt.packLong(initSize); // allocation size m_pkt.packInt(attrib); // file attributes m_pkt.packInt(sharing); // share access mode m_pkt.packInt(exists); // action to take if file exists m_pkt.packInt(createOpt); // file create options m_pkt.packInt(2); // impersonation level, 0=anonymous, 2=impersonation m_pkt.packByte(0); // security flags m_pkt.resetBytePointer(); m_pkt.packString(name, m_pkt.isUnicode()); m_pkt.setByteCount(); // Send/receive the NT create andX request m_pkt.ExchangeSMB(this, m_pkt, throwErr); // Unpack the file/directory details m_pkt.resetParameterPointer(); m_pkt.skipBytes(4); int oplockTyp = m_pkt.unpackByte(); int fid = m_pkt.unpackWord(); int createAction = m_pkt.unpackInt(); long createTime = m_pkt.unpackLong(); long lastAccessTime = m_pkt.unpackLong(); long lastWriteTime = m_pkt.unpackLong(); long changeTime = m_pkt.unpackLong(); int attr = m_pkt.unpackInt(); long allocSize = m_pkt.unpackLong(); long eofOffset = m_pkt.unpackLong(); int devType = m_pkt.unpackWord(); // Create the file information FileInfo finfo = new FileInfo(name, eofOffset, attr); finfo.setFileId(fid); // Convert the granted oplock type to internal type if ( oplockTyp == WinNT.GrantedOplockBatch) oplockTyp = OpLock.TypeBatch; else if ( oplockTyp == WinNT.GrantedOplockExclusive) oplockTyp = OpLock.TypeExclusive; else if ( oplockTyp == WinNT.GrantedOplockLevelII) oplockTyp = OpLock.TypeLevelII; else oplockTyp = OpLock.TypeNone; // Create the file object return new CIFSFile(this, finfo, fid, oplockTyp); } /** * File closed, remove from the oplocked file list * * @param cifsFile CIFSFile */ protected final void fileClosed( CIFSFile cifsFile) { // Check if there are any oplocked files if ( m_oplockFiles == null || m_oplockFiles.size() == 0) return; // Remove the file from the oplocked list m_oplockFiles.remove( new Integer( cifsFile.getFileId())); } }