jellyfin-server/Emby.Common.Implementations/IO/SharpCifs/Smb/SmbTree.cs
2017-04-01 20:36:06 -04:00

251 lines
6.3 KiB
C#

// This code is derived from jcifs smb client library <jcifs at samba dot org>
// Ported by J. Arturo <webmaster at komodosoft dot net>
//
// This library 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 2.1 of the License, or (at your option) any later version.
//
// This library 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 this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
using System;
using SharpCifs.Util.Sharpen;
namespace SharpCifs.Smb
{
class SmbTree
{
private static int _treeConnCounter;
internal int ConnectionState;
internal int Tid;
internal string Share;
internal string Service = "?????";
internal string Service0;
internal SmbSession Session;
internal bool InDfs;
internal bool InDomainDfs;
internal int TreeNum;
internal SmbTree(SmbSession session, string share, string service)
{
// used by SmbFile.isOpen
this.Session = session;
this.Share = share.ToUpper();
if (service != null && service.StartsWith("??") == false)
{
this.Service = service;
}
Service0 = this.Service;
ConnectionState = 0;
}
internal virtual bool Matches(string share, string service)
{
return Runtime.EqualsIgnoreCase(this.Share, share) && (service == null ||
service.StartsWith("??") || Runtime.EqualsIgnoreCase(this.Service, service
));
}
public override bool Equals(object obj)
{
if (obj is SmbTree)
{
SmbTree tree = (SmbTree)obj;
return Matches(tree.Share, tree.Service);
}
return false;
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
internal virtual void Send(ServerMessageBlock request, ServerMessageBlock response
)
{
lock (Session.Transport())
{
if (response != null)
{
response.Received = false;
}
TreeConnect(request, response);
if (request == null || (response != null && response.Received))
{
return;
}
if (Service.Equals("A:") == false)
{
switch (request.Command)
{
case ServerMessageBlock.SmbComOpenAndx:
case ServerMessageBlock.SmbComNtCreateAndx:
case ServerMessageBlock.SmbComReadAndx:
case ServerMessageBlock.SmbComWriteAndx:
case ServerMessageBlock.SmbComClose:
case ServerMessageBlock.SmbComTreeDisconnect:
{
break;
}
case ServerMessageBlock.SmbComTransaction:
case ServerMessageBlock.SmbComTransaction2:
{
switch (((SmbComTransaction)request).SubCommand & unchecked(0xFF))
{
case SmbComTransaction.NetShareEnum:
case SmbComTransaction.NetServerEnum2:
case SmbComTransaction.NetServerEnum3:
case SmbComTransaction.TransPeekNamedPipe:
case SmbComTransaction.TransWaitNamedPipe:
case SmbComTransaction.TransCallNamedPipe:
case SmbComTransaction.TransTransactNamedPipe:
case SmbComTransaction.Trans2GetDfsReferral:
{
break;
}
default:
{
throw new SmbException("Invalid operation for " + Service + " service");
}
}
break;
}
default:
{
throw new SmbException("Invalid operation for " + Service + " service" + request);
}
}
}
request.Tid = Tid;
if (InDfs && !Service.Equals("IPC") && !string.IsNullOrEmpty(request.Path))
{
request.Flags2 = SmbConstants.Flags2ResolvePathsInDfs;
request.Path = '\\' + Session.Transport().TconHostName + '\\' + Share + request.Path;
}
try
{
Session.Send(request, response);
}
catch (SmbException se)
{
if (se.GetNtStatus() == NtStatus.NtStatusNetworkNameDeleted)
{
TreeDisconnect(true);
}
throw;
}
}
}
/// <exception cref="SharpCifs.Smb.SmbException"></exception>
internal virtual void TreeConnect(ServerMessageBlock andx, ServerMessageBlock andxResponse
)
{
lock (Session.Transport())
{
string unc;
while (ConnectionState != 0)
{
if (ConnectionState == 2 || ConnectionState == 3)
{
// connected or disconnecting
return;
}
try
{
Runtime.Wait(Session.transport);
}
catch (Exception ie)
{
throw new SmbException(ie.Message, ie);
}
}
ConnectionState = 1;
// trying ...
try
{
Session.transport.Connect();
unc = "\\\\" + Session.transport.TconHostName + '\\' + Share;
Service = Service0;
if (Session.transport.Log.Level >= 4)
{
Session.transport.Log.WriteLine("treeConnect: unc=" + unc + ",service=" + Service
);
}
SmbComTreeConnectAndXResponse response = new SmbComTreeConnectAndXResponse(andxResponse
);
SmbComTreeConnectAndX request = new SmbComTreeConnectAndX(Session, unc, Service,
andx);
Session.Send(request, response);
Tid = response.Tid;
Service = response.Service;
InDfs = response.ShareIsInDfs;
TreeNum = _treeConnCounter++;
ConnectionState = 2;
}
catch (SmbException se)
{
// connected
TreeDisconnect(true);
ConnectionState = 0;
throw;
}
}
}
internal virtual void TreeDisconnect(bool inError)
{
lock (Session.Transport())
{
if (ConnectionState != 2)
{
// not-connected
return;
}
ConnectionState = 3;
// disconnecting
if (!inError && Tid != 0)
{
try
{
Send(new SmbComTreeDisconnect(), null);
}
catch (SmbException se)
{
if (Session.transport.Log.Level > 1)
{
Runtime.PrintStackTrace(se, Session.transport.Log);
}
}
}
InDfs = false;
InDomainDfs = false;
ConnectionState = 0;
Runtime.NotifyAll(Session.transport);
}
}
public override string ToString()
{
return "SmbTree[share=" + Share + ",service=" + Service + ",tid=" + Tid + ",inDfs="
+ InDfs + ",inDomainDfs=" + InDomainDfs + ",connectionState=" + ConnectionState
+ "]";
}
}
}