mirror of
https://github.com/Alfresco/alfresco-community-repo.git
synced 2025-06-09 17:45:10 +00:00
git-svn-id: https://svn.alfresco.com/repos/alfresco-enterprise/alfresco/HEAD/root@2748 c4b6b30b-aa2e-2d43-bbcb-ca4b014f7261
597 lines
21 KiB
Java
597 lines
21 KiB
Java
/*
|
|
* Copyright (C) 2005 Alfresco, Inc.
|
|
*
|
|
* Licensed under the Alfresco Network License. You may obtain a
|
|
* copy of the License at
|
|
*
|
|
* http://www.alfrescosoftware.com/legal/
|
|
*
|
|
* Please view the license relevant to your network subscription.
|
|
*
|
|
* BY CLICKING THE "I UNDERSTAND AND ACCEPT" BOX, OR INSTALLING,
|
|
* READING OR USING ALFRESCO'S Network SOFTWARE (THE "SOFTWARE"),
|
|
* YOU ARE AGREEING ON BEHALF OF THE ENTITY LICENSING THE SOFTWARE
|
|
* ("COMPANY") THAT COMPANY WILL BE BOUND BY AND IS BECOMING A PARTY TO
|
|
* THIS ALFRESCO NETWORK AGREEMENT ("AGREEMENT") AND THAT YOU HAVE THE
|
|
* AUTHORITY TO BIND COMPANY. IF COMPANY DOES NOT AGREE TO ALL OF THE
|
|
* TERMS OF THIS AGREEMENT, DO NOT SELECT THE "I UNDERSTAND AND AGREE"
|
|
* BOX AND DO NOT INSTALL THE SOFTWARE OR VIEW THE SOURCE CODE. COMPANY
|
|
* HAS NOT BECOME A LICENSEE OF, AND IS NOT AUTHORIZED TO USE THE
|
|
* SOFTWARE UNLESS AND UNTIL IT HAS AGREED TO BE BOUND BY THESE LICENSE
|
|
* TERMS. THE "EFFECTIVE DATE" FOR THIS AGREEMENT SHALL BE THE DAY YOU
|
|
* CHECK THE "I UNDERSTAND AND ACCEPT" BOX.
|
|
*/
|
|
package org.alfresco.filesys.server.smb.repo;
|
|
|
|
import java.io.FileNotFoundException;
|
|
|
|
import org.alfresco.filesys.server.SrvSession;
|
|
import org.alfresco.filesys.server.filesys.DiskDeviceContext;
|
|
import org.alfresco.filesys.server.filesys.FileName;
|
|
import org.alfresco.filesys.server.filesys.IOControlNotImplementedException;
|
|
import org.alfresco.filesys.server.filesys.NetworkFile;
|
|
import org.alfresco.filesys.server.filesys.NotifyChange;
|
|
import org.alfresco.filesys.server.filesys.TreeConnection;
|
|
import org.alfresco.filesys.smb.NTIOCtl;
|
|
import org.alfresco.filesys.smb.SMBException;
|
|
import org.alfresco.filesys.smb.SMBStatus;
|
|
import org.alfresco.filesys.smb.server.repo.CifsHelper;
|
|
import org.alfresco.filesys.smb.server.repo.ContentDiskDriver;
|
|
import org.alfresco.filesys.smb.server.repo.IOControlHandler;
|
|
import org.alfresco.filesys.util.DataBuffer;
|
|
import org.alfresco.model.ContentModel;
|
|
import org.alfresco.service.cmr.coci.CheckOutCheckInService;
|
|
import org.alfresco.service.cmr.lock.LockType;
|
|
import org.alfresco.service.cmr.repository.ContentData;
|
|
import org.alfresco.service.cmr.repository.NodeRef;
|
|
import org.alfresco.service.cmr.repository.NodeService;
|
|
import org.alfresco.service.transaction.TransactionService;
|
|
import org.apache.commons.logging.Log;
|
|
import org.apache.commons.logging.LogFactory;
|
|
|
|
/**
|
|
* Content Disk Driver I/O Control Handler Class
|
|
*
|
|
* <p>Provides the custom I/O control code handling used by the CIFS client interface application.
|
|
*
|
|
* @author gkspencer
|
|
*/
|
|
public class ContentIOControlHandler implements IOControlHandler
|
|
{
|
|
// Logging
|
|
|
|
private static final Log logger = LogFactory.getLog(ContentIOControlHandler.class);
|
|
|
|
// Services and helpers
|
|
|
|
private CifsHelper cifsHelper;
|
|
private TransactionService transactionService;
|
|
private NodeService nodeService;
|
|
private CheckOutCheckInService checkInOutService;
|
|
|
|
private ContentDiskDriver contentDriver;
|
|
|
|
/**
|
|
* Default constructor
|
|
*/
|
|
public ContentIOControlHandler()
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Initalize the I/O control handler
|
|
*
|
|
* @param contentDriver ContentDiskDriver
|
|
* @param cifsHelper CifsHelper
|
|
* @param transService TransactionService
|
|
* @param nodeService NodeService
|
|
* @param cociService CheckOutCheckInService
|
|
*/
|
|
public void initialize( ContentDiskDriver contentDriver, CifsHelper cifsHelper,
|
|
TransactionService transService, NodeService nodeService, CheckOutCheckInService cociService)
|
|
{
|
|
this.contentDriver = contentDriver;
|
|
this.cifsHelper = cifsHelper;
|
|
this.transactionService = transService;
|
|
this.nodeService = nodeService;
|
|
this.checkInOutService = cociService;
|
|
}
|
|
|
|
/**
|
|
* Process a filesystem I/O control request
|
|
*
|
|
* @param sess Server session
|
|
* @param tree Tree connection.
|
|
* @param ctrlCode I/O control code
|
|
* @param fid File id
|
|
* @param dataBuf I/O control specific input data
|
|
* @param isFSCtrl true if this is a filesystem control, or false for a device control
|
|
* @param filter if bit0 is set indicates that the control applies to the share root handle
|
|
* @return DataBuffer
|
|
* @exception IOControlNotImplementedException
|
|
* @exception SMBException
|
|
*/
|
|
public DataBuffer processIOControl(SrvSession sess, TreeConnection tree, int ctrlCode, int fid, DataBuffer dataBuf,
|
|
boolean isFSCtrl, int filter)
|
|
throws IOControlNotImplementedException, SMBException
|
|
{
|
|
// Validate the file id
|
|
|
|
NetworkFile netFile = tree.findFile(fid);
|
|
if ( netFile == null || netFile.isDirectory() == false)
|
|
throw new SMBException(SMBStatus.NTErr, SMBStatus.NTInvalidParameter);
|
|
|
|
// Split the control code
|
|
|
|
int devType = NTIOCtl.getDeviceType(ctrlCode);
|
|
int ioFunc = NTIOCtl.getFunctionCode(ctrlCode);
|
|
|
|
if ( devType != NTIOCtl.DeviceFileSystem || dataBuf == null)
|
|
throw new IOControlNotImplementedException();
|
|
|
|
// Check if the request has a valid signature for an Alfresco CIFS server I/O control
|
|
|
|
if ( dataBuf.getLength() < IOControl.Signature.length())
|
|
throw new IOControlNotImplementedException("Bad request length");
|
|
|
|
String sig = dataBuf.getString(IOControl.Signature.length(), false);
|
|
|
|
if ( sig == null || sig.compareTo(IOControl.Signature) != 0)
|
|
throw new IOControlNotImplementedException("Bad request signature");
|
|
|
|
// Get the node for the parent folder, make sure it is a folder
|
|
|
|
NodeRef folderNode = null;
|
|
|
|
try
|
|
{
|
|
folderNode = contentDriver.getNodeForPath(tree, netFile.getFullName());
|
|
|
|
if ( cifsHelper.isDirectory( folderNode) == false)
|
|
folderNode = null;
|
|
}
|
|
catch ( FileNotFoundException ex)
|
|
{
|
|
folderNode = null;
|
|
}
|
|
|
|
// If the folder node is not valid return an error
|
|
|
|
if ( folderNode == null)
|
|
throw new SMBException(SMBStatus.NTErr, SMBStatus.NTAccessDenied);
|
|
|
|
// Debug
|
|
|
|
if ( logger.isInfoEnabled()) {
|
|
logger.info("IO control func=0x" + Integer.toHexString(ioFunc) + ", fid=" + fid + ", buffer=" + dataBuf);
|
|
logger.info(" Folder nodeRef=" + folderNode);
|
|
}
|
|
|
|
// Check if the I/O control code is one of our custom codes
|
|
|
|
DataBuffer retBuffer = null;
|
|
|
|
switch ( ioFunc)
|
|
{
|
|
// Probe to check if this is an Alfresco CIFS server
|
|
|
|
case IOControl.CmdProbe:
|
|
|
|
// Return a buffer with the signature
|
|
|
|
retBuffer = new DataBuffer(IOControl.Signature.length());
|
|
retBuffer.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
|
retBuffer.putInt(IOControl.StsSuccess);
|
|
break;
|
|
|
|
// Get file information for a file within the current folder
|
|
|
|
case IOControl.CmdFileStatus:
|
|
|
|
// Process the file status request
|
|
|
|
retBuffer = procIOFileStatus( sess, tree, dataBuf, folderNode);
|
|
break;
|
|
|
|
// Check-in file request
|
|
|
|
case IOControl.CmdCheckIn:
|
|
|
|
// Process the check-in request
|
|
|
|
retBuffer = procIOCheckIn( sess, tree, dataBuf, folderNode, netFile);
|
|
break;
|
|
|
|
// Check-out file request
|
|
|
|
case IOControl.CmdCheckOut:
|
|
|
|
// Process the check-out request
|
|
|
|
retBuffer = procIOCheckOut( sess, tree, dataBuf, folderNode, netFile);
|
|
break;
|
|
|
|
// Unknown I/O control code
|
|
|
|
default:
|
|
throw new IOControlNotImplementedException();
|
|
}
|
|
|
|
// Return the reply buffer, may be null
|
|
|
|
return retBuffer;
|
|
}
|
|
|
|
/**
|
|
* Process the file status I/O request
|
|
*
|
|
* @param sess Server session
|
|
* @param tree Tree connection
|
|
* @param reqBuf Request buffer
|
|
* @param folderNode NodeRef of parent folder
|
|
* @return DataBuffer
|
|
*/
|
|
private final DataBuffer procIOFileStatus( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode)
|
|
{
|
|
// Start a transaction
|
|
|
|
sess.beginTransaction( transactionService, true);
|
|
|
|
// Get the file name from the request
|
|
|
|
String fName = reqBuf.getString( true);
|
|
logger.info(" File status, fname=" + fName);
|
|
|
|
// Create a response buffer
|
|
|
|
DataBuffer respBuf = new DataBuffer(256);
|
|
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
|
|
|
// Get the node for the file/folder
|
|
|
|
NodeRef childNode = null;
|
|
|
|
try
|
|
{
|
|
childNode = cifsHelper.getNodeRef( folderNode, fName);
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
}
|
|
|
|
// Check if the file/folder was found
|
|
|
|
if ( childNode == null)
|
|
{
|
|
// Return an error response
|
|
|
|
respBuf.putInt(IOControl.StsFileNotFound);
|
|
return respBuf;
|
|
}
|
|
|
|
// Check if this is a file or folder node
|
|
|
|
if ( cifsHelper.isDirectory( childNode))
|
|
{
|
|
// Only return the status and node type for folders
|
|
|
|
respBuf.putInt(IOControl.StsSuccess);
|
|
respBuf.putInt(IOControl.TypeFolder);
|
|
}
|
|
else
|
|
{
|
|
// Indicate that this is a file node
|
|
|
|
respBuf.putInt(IOControl.StsSuccess);
|
|
respBuf.putInt(IOControl.TypeFile);
|
|
|
|
// Check if this file is a working copy
|
|
|
|
if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY))
|
|
{
|
|
// Indicate that this is a working copy
|
|
|
|
respBuf.putInt(IOControl.True);
|
|
|
|
// Get the owner username and file it was copied from
|
|
|
|
String owner = (String) nodeService.getProperty( childNode, ContentModel.PROP_WORKING_COPY_OWNER);
|
|
String copiedFrom = null;
|
|
|
|
if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_COPIEDFROM))
|
|
{
|
|
// Get the path of the file the working copy was generated from
|
|
|
|
NodeRef fromNode = (NodeRef) nodeService.getProperty( childNode, ContentModel.PROP_COPY_REFERENCE);
|
|
if ( fromNode != null)
|
|
copiedFrom = (String) nodeService.getProperty( fromNode, ContentModel.PROP_NAME);
|
|
}
|
|
|
|
// Pack the owner and copied from values
|
|
|
|
respBuf.putString(owner != null ? owner : "", true, true);
|
|
respBuf.putString(copiedFrom != null ? copiedFrom : "", true, true);
|
|
}
|
|
else
|
|
{
|
|
// Not a working copy
|
|
|
|
respBuf.putInt(IOControl.False);
|
|
}
|
|
|
|
// Check the lock status of the file
|
|
|
|
if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_LOCKABLE))
|
|
{
|
|
// Get the lock type and owner
|
|
|
|
String lockTypeStr = (String) nodeService.getProperty( childNode, ContentModel.PROP_LOCK_TYPE);
|
|
String lockOwner = null;
|
|
|
|
if ( lockTypeStr != null)
|
|
lockOwner = (String) nodeService.getProperty( childNode, ContentModel.PROP_LOCK_OWNER);
|
|
|
|
// Pack the lock type, and owner if there is a lock on the file
|
|
|
|
if ( lockTypeStr == null)
|
|
respBuf.putInt(IOControl.LockNone);
|
|
else
|
|
{
|
|
LockType lockType = LockType.valueOf( lockTypeStr);
|
|
|
|
respBuf.putInt(lockType == LockType.READ_ONLY_LOCK ? IOControl.LockRead : IOControl.LockWrite);
|
|
respBuf.putString(lockOwner != null ? lockOwner : "", true, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// File is not lockable
|
|
|
|
respBuf.putInt(IOControl.LockNone);
|
|
}
|
|
|
|
// Get the content data details for the file
|
|
|
|
ContentData contentData = (ContentData) nodeService.getProperty( childNode, ContentModel.PROP_CONTENT);
|
|
|
|
if ( contentData != null)
|
|
{
|
|
// Get the content mime-type
|
|
|
|
String mimeType = contentData.getMimetype();
|
|
|
|
// Pack the content length and mime-type
|
|
|
|
respBuf.putInt( IOControl.True);
|
|
respBuf.putLong( contentData.getSize());
|
|
respBuf.putString( mimeType != null ? mimeType : "", true, true);
|
|
}
|
|
else
|
|
{
|
|
// File does not have any content
|
|
|
|
respBuf.putInt( IOControl.False);
|
|
}
|
|
}
|
|
|
|
// Return the response
|
|
|
|
return respBuf;
|
|
}
|
|
|
|
/**
|
|
* Process the check in I/O request
|
|
*
|
|
* @param sess Server session
|
|
* @param tree Tree connection
|
|
* @param reqBuf Request buffer
|
|
* @param folderNode NodeRef of parent folder
|
|
* @param netFile NetworkFile for the folder
|
|
* @return DataBuffer
|
|
*/
|
|
private final DataBuffer procIOCheckIn( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
|
|
NetworkFile netFile)
|
|
{
|
|
// Start a transaction
|
|
|
|
sess.beginTransaction( transactionService, false);
|
|
|
|
// Get the file name from the request
|
|
|
|
String fName = reqBuf.getString( true);
|
|
boolean keepCheckedOut = reqBuf.getInt() == IOControl.True ? true : false;
|
|
|
|
logger.info(" CheckIn, fname=" + fName + ", keepCheckedOut=" + keepCheckedOut);
|
|
|
|
// Create a response buffer
|
|
|
|
DataBuffer respBuf = new DataBuffer(256);
|
|
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
|
|
|
// Get the node for the file/folder
|
|
|
|
NodeRef childNode = null;
|
|
|
|
try
|
|
{
|
|
childNode = cifsHelper.getNodeRef( folderNode, fName);
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
}
|
|
|
|
// Check if the file/folder was found
|
|
|
|
if ( childNode == null)
|
|
{
|
|
// Return an error response
|
|
|
|
respBuf.putInt(IOControl.StsFileNotFound);
|
|
return respBuf;
|
|
}
|
|
|
|
// Check if this is a file or folder node
|
|
|
|
if ( cifsHelper.isDirectory( childNode))
|
|
{
|
|
// Return an error status, attempt to check in a folder
|
|
|
|
respBuf.putInt(IOControl.StsBadParameter);
|
|
}
|
|
else
|
|
{
|
|
// Check if this file is a working copy
|
|
|
|
if ( nodeService.hasAspect( childNode, ContentModel.ASPECT_WORKING_COPY))
|
|
{
|
|
try
|
|
{
|
|
// Check in the file
|
|
|
|
checkInOutService.checkin( childNode, null, null, keepCheckedOut);
|
|
|
|
// Check in was successful
|
|
|
|
respBuf.putInt( IOControl.StsSuccess);
|
|
|
|
// Check if there are any file/directory change notify requests active
|
|
|
|
DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext();
|
|
if (diskCtx.hasChangeHandler()) {
|
|
|
|
// Build the relative path to the checked in file
|
|
|
|
String fileName = FileName.buildPath( netFile.getFullName(), null, fName, FileName.DOS_SEPERATOR);
|
|
|
|
// Queue a file deleted change notification
|
|
|
|
diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionRemoved, fileName);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Return an error status and message
|
|
|
|
respBuf.setPosition( IOControl.Signature.length());
|
|
respBuf.putInt(IOControl.StsError);
|
|
respBuf.putString( ex.getMessage(), true, true);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Not a working copy
|
|
|
|
respBuf.putInt(IOControl.StsNotWorkingCopy);
|
|
}
|
|
}
|
|
|
|
// Return the response
|
|
|
|
return respBuf;
|
|
}
|
|
|
|
/**
|
|
* Process the check out I/O request
|
|
*
|
|
* @param sess Server session
|
|
* @param tree Tree connection
|
|
* @param reqBuf Request buffer
|
|
* @param folderNode NodeRef of parent folder
|
|
* @param netFile NetworkFile for the folder
|
|
* @return DataBuffer
|
|
*/
|
|
private final DataBuffer procIOCheckOut( SrvSession sess, TreeConnection tree, DataBuffer reqBuf, NodeRef folderNode,
|
|
NetworkFile netFile)
|
|
{
|
|
// Start a transaction
|
|
|
|
sess.beginTransaction( transactionService, false);
|
|
|
|
// Get the file name from the request
|
|
|
|
String fName = reqBuf.getString( true);
|
|
|
|
logger.info(" CheckOut, fname=" + fName);
|
|
|
|
// Create a response buffer
|
|
|
|
DataBuffer respBuf = new DataBuffer(256);
|
|
respBuf.putFixedString(IOControl.Signature, IOControl.Signature.length());
|
|
|
|
// Get the node for the file/folder
|
|
|
|
NodeRef childNode = null;
|
|
|
|
try
|
|
{
|
|
childNode = cifsHelper.getNodeRef( folderNode, fName);
|
|
}
|
|
catch (FileNotFoundException ex)
|
|
{
|
|
}
|
|
|
|
// Check if the file/folder was found
|
|
|
|
if ( childNode == null)
|
|
{
|
|
// Return an error response
|
|
|
|
respBuf.putInt(IOControl.StsFileNotFound);
|
|
return respBuf;
|
|
}
|
|
|
|
// Check if this is a file or folder node
|
|
|
|
if ( cifsHelper.isDirectory( childNode))
|
|
{
|
|
// Return an error status, attempt to check in a folder
|
|
|
|
respBuf.putInt(IOControl.StsBadParameter);
|
|
}
|
|
else
|
|
{
|
|
try
|
|
{
|
|
// Check out the file
|
|
|
|
NodeRef workingCopyNode = checkInOutService.checkout( childNode);
|
|
|
|
// Get the working copy file name
|
|
|
|
String workingCopyName = (String) nodeService.getProperty( workingCopyNode, ContentModel.PROP_NAME);
|
|
|
|
// Check out was successful, pack the working copy name
|
|
|
|
respBuf.putInt( IOControl.StsSuccess);
|
|
respBuf.putString( workingCopyName, true, true);
|
|
|
|
// Check if there are any file/directory change notify requests active
|
|
|
|
DiskDeviceContext diskCtx = (DiskDeviceContext) tree.getContext();
|
|
if (diskCtx.hasChangeHandler()) {
|
|
|
|
// Build the relative path to the checked in file
|
|
|
|
String fileName = FileName.buildPath( netFile.getFullName(), null, workingCopyName, FileName.DOS_SEPERATOR);
|
|
|
|
// Queue a file added change notification
|
|
|
|
diskCtx.getChangeHandler().notifyFileChanged(NotifyChange.ActionAdded, fileName);
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
// Return an error status and message
|
|
|
|
respBuf.setPosition( IOControl.Signature.length());
|
|
respBuf.putInt(IOControl.StsError);
|
|
respBuf.putString( ex.getMessage(), true, true);
|
|
}
|
|
}
|
|
|
|
// Return the response
|
|
|
|
return respBuf;
|
|
}
|
|
}
|