jcifs.smb.SmbFile Maven / Gradle / Ivy
Show all versions of jcifs-ng Show documentation
/* jcifs smb client library in Java
* Copyright (C) 2000 "Michael B. Allen"
*
* 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
*/
package jcifs.smb;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Random;
import java.util.Set;
import org.apache.log4j.Logger;
import jcifs.CIFSContext;
import jcifs.RuntimeCIFSException;
import jcifs.SmbConstants;
import jcifs.context.SingletonContext;
import jcifs.dcerpc.DcerpcHandle;
import jcifs.dcerpc.msrpc.MsrpcDfsRootEnum;
import jcifs.dcerpc.msrpc.MsrpcShareEnum;
import jcifs.dcerpc.msrpc.MsrpcShareGetInfo;
import jcifs.netbios.NbtAddress;
import jcifs.netbios.UniAddress;
/**
* This class represents a resource on an SMB network. Mainly these
* resources are files and directories however an SmbFile
* may also refer to servers and workgroups. If the resource is a file or
* directory the methods of SmbFile
follow the behavior of
* the well known {@link java.io.File} class. One fundamental difference
* is the usage of a URL scheme [1] to specify the target file or
* directory. SmbFile URLs have the following syntax:
*
*
*
*
* smb://[[[domain;]username[:password]@]server[:port]/[[share/[dir/]file]]][?param=value[param2=value2[...]]]
*
*
*
*
* This example:
*
*
*
*
* smb://storage15/public/foo.txt
*
*
*
*
* would reference the file foo.txt
in the share
* public
on the server storage15
. In addition
* to referencing files and directories, jCIFS can also address servers,
* and workgroups.
*
* Important: all SMB URLs that represent
* workgroups, servers, shares, or directories require a trailing slash '/'.
*
*
* When using the java.net.URL class with
* 'smb://' URLs it is necessary to first call the static
* jcifs.Config.registerSmbURLHandler(); method. This is required
* to register the SMB protocol handler.
*
* The userinfo component of the SMB URL (domain;user:pass) must
* be URL encoded if it contains reserved characters. According to RFC 2396
* these characters are non US-ASCII characters and most meta characters
* however jCIFS will work correctly with anything but '@' which is used
* to delimit the userinfo component from the server and '%' which is the
* URL escape character itself.
*
* The server
* component may a traditional NetBIOS name, a DNS name, or IP
* address. These name resolution mechanisms and their resolution order
* can be changed (See Setting Name
* Resolution Properties). The servername and path components are
* not case sensitive but the domain, username, and password components
* are. It is also likely that properties must be specified for jcifs
* to function (See Setting
* JCIFS Properties). Here are some examples of SMB URLs with brief
* descriptions of what they do:
*
*
* [1] This URL scheme is based largely on the SMB
* Filesharing URL Scheme IETF draft.
*
*
*
*
* SMB URL Examples
*
* URL
* Description
*
*
*
* smb://users-nyc;miallen:mypass@angus/tmp/
*
* This URL references a share called tmp
on the server
* angus
as user miallen
who's password is
* mypass
.
*
*
*
*
*
* smb://Administrator:P%40ss@msmith1/c/WINDOWS/Desktop/foo.txt
*
* A relativly sophisticated example that references a file
* msmith1
's desktop as user Administrator
. Notice the '@' is URL encoded with the '%40'
* hexcode escape.
*
*
*
*
* smb://angus/
*
* This references only a server. The behavior of some methods is different
* in this context(e.g. you cannot delete
a server) however
* as you might expect the list
method will list the available
* shares on this server.
*
*
*
*
* smb://myworkgroup/
*
* This syntactically is identical to the above example. However if
* myworkgroup
happends to be a workgroup(which is indeed
* suggested by the name) the list
method will return
* a list of servers that have registered themselves as members of
* myworkgroup
.
*
*
*
*
* smb://
*
* Just as smb://server/
lists shares and
* smb://workgroup/
lists servers, the smb://
* URL lists all available workgroups on a netbios LAN. Again,
* in this context many methods are not valid and return default
* values(e.g. isHidden
will always return false).
*
*
*
*
* smb://angus.foo.net/d/jcifs/pipes.doc
*
* The server name may also be a DNS name as it is in this example. See
* Setting Name Resolution Properties
* for details.
*
*
*
*
* smb://192.168.1.15/ADMIN$/
*
* The server name may also be an IP address. See Setting Name Resolution Properties
* for details.
*
*
*
*
*
* smb://domain;username:password@server/share/path/to/file.txt
*
* A prototypical example that uses all the fields.
*
*
*
*
* smb://myworkgroup/angus/ <-- ILLEGAL
*
* Despite the hierarchial relationship between workgroups, servers, and
* filesystems this example is not valid.
*
*
*
*
*
* smb://server/share/path/to/dir <-- ILLEGAL
*
* URLs that represent workgroups, servers, shares, or directories require a trailing slash '/'.
*
*
*
*
*
* smb://MYGROUP/?SERVER=192.168.10.15
*
* SMB URLs support some query string parameters. In this example
* the SERVER
parameter is used to override the
* server name service lookup to contact the server 192.168.10.15
* (presumably known to be a master
* browser) for the server list in workgroup MYGROUP
.
*
*
*
*
*
*
* A second constructor argument may be specified to augment the URL
* for better programmatic control when processing many files under
* a common base. This is slightly different from the corresponding
* java.io.File
usage; a '/' at the beginning of the second
* parameter will still use the server component of the first parameter. The
* examples below illustrate the resulting URLs when this second contructor
* argument is used.
*
*
*
*
*
* Examples Of SMB URLs When Augmented With A Second Constructor Parameter
*
*
* First Parameter
* Second Parameter
* Result
*
*
*
*
* smb://host/share/a/b/
*
*
* c/d/
*
*
* smb://host/share/a/b/c/d/
*
*
*
*
*
* smb://host/share/foo/bar/
*
*
* /share2/zig/zag
*
*
* smb://host/share2/zig/zag
*
*
*
*
*
* smb://host/share/foo/bar/
*
*
* ../zip/
*
*
* smb://host/share/foo/zip/
*
*
*
*
*
* smb://host/share/zig/zag
*
*
* smb://foo/bar/
*
*
* smb://foo/bar/
*
*
*
*
*
* smb://host/share/foo/
*
*
* ../.././.././../foo/
*
*
* smb://host/foo/
*
*
*
*
*
* smb://host/share/zig/zag
*
*
* /
*
*
* smb://host/
*
*
*
*
*
* smb://server/
*
*
* ../
*
*
* smb://server/
*
*
*
*
*
* smb://
*
*
* myworkgroup/
*
*
* smb://myworkgroup/
*
*
*
*
*
* smb://myworkgroup/
*
*
* angus/
*
*
* smb://myworkgroup/angus/ <-- ILLEGAL
(But if you first create an SmbFile with 'smb://workgroup/' and
* use and use it as the first parameter to a constructor that accepts it with a second String parameter jCIFS
* will factor out the 'workgroup'.)
*
*
*
*
*
*
* Instances of the SmbFile
class are immutable; that is,
* once created, the abstract pathname represented by an SmbFile object
* will never change.
*
* @see java.io.File
*/
public class SmbFile extends URLConnection implements SmbConstants {
static final int O_RDONLY = 0x01;
static final int O_WRONLY = 0x02;
static final int O_RDWR = 0x03;
static final int O_APPEND = 0x04;
// Open Function Encoding
// create if the file does not exist
static final int O_CREAT = 0x0010;
// fail if the file exists
static final int O_EXCL = 0x0020;
// truncate if the file exists
static final int O_TRUNC = 0x0040;
// share access
/**
* When specified as the shareAccess constructor parameter,
* other SMB clients (including other threads making calls into jCIFS)
* will not be permitted to access the target file and will receive "The
* file is being accessed by another process" message.
*/
public static final int FILE_NO_SHARE = 0x00;
/**
* When specified as the shareAccess constructor parameter,
* other SMB clients will be permitted to read from the target file while
* this file is open. This constant may be logically OR'd with other share
* access flags.
*/
public static final int FILE_SHARE_READ = 0x01;
/**
* When specified as the shareAccess constructor parameter,
* other SMB clients will be permitted to write to the target file while
* this file is open. This constant may be logically OR'd with other share
* access flags.
*/
public static final int FILE_SHARE_WRITE = 0x02;
/**
* When specified as the shareAccess constructor parameter,
* other SMB clients will be permitted to delete the target file while
* this file is open. This constant may be logically OR'd with other share
* access flags.
*/
public static final int FILE_SHARE_DELETE = 0x04;
// file attribute encoding
/**
* A file with this bit on as returned by getAttributes() or set
* with setAttributes() will be read-only
*/
public static final int ATTR_READONLY = 0x01;
/**
* A file with this bit on as returned by getAttributes() or set
* with setAttributes() will be hidden
*/
public static final int ATTR_HIDDEN = 0x02;
/**
* A file with this bit on as returned by getAttributes() or set
* with setAttributes() will be a system file
*/
public static final int ATTR_SYSTEM = 0x04;
/**
* A file with this bit on as returned by getAttributes() is
* a volume
*/
public static final int ATTR_VOLUME = 0x08;
/**
* A file with this bit on as returned by getAttributes() is
* a directory
*/
public static final int ATTR_DIRECTORY = 0x10;
/**
* A file with this bit on as returned by getAttributes() or set
* with setAttributes() is an archived file
*/
public static final int ATTR_ARCHIVE = 0x20;
// extended file attribute encoding(others same as above)
static final int ATTR_COMPRESSED = 0x800;
static final int ATTR_NORMAL = 0x080;
static final int ATTR_TEMPORARY = 0x100;
static final int ATTR_GET_MASK = 0x7FFF; /* orig 0x7fff */
static final int ATTR_SET_MASK = 0x30A7; /* orig 0x0027 */
static final int DEFAULT_ATTR_EXPIRATION_PERIOD = 5000;
static final int HASH_DOT = ".".hashCode();
static final int HASH_DOT_DOT = "..".hashCode();
static Logger log = Logger.getLogger(SmbFile.class);
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a regular file or directory.
*/
public static final int TYPE_FILESYSTEM = 0x01;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a workgroup.
*/
public static final int TYPE_WORKGROUP = 0x02;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a server.
*/
public static final int TYPE_SERVER = 0x04;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a share.
*/
public static final int TYPE_SHARE = 0x08;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a named pipe.
*/
public static final int TYPE_NAMED_PIPE = 0x10;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a printer.
*/
public static final int TYPE_PRINTER = 0x20;
/**
* Returned by {@link #getType()} if the resource this SmbFile
* represents is a communications device.
*/
public static final int TYPE_COMM = 0x40;
private String canon; // Initially null; set by getUncPath; dir must end with '/'
private String share; // Can be null
private long createTime;
private long lastModified;
private long lastAccess;
private int attributes;
private long attrExpiration;
private long size;
private long sizeExpiration;
private boolean isExists;
private int shareAccess = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE;
private SmbComBlankResponse blank_resp = null;
private DfsReferral dfsReferral = null; // For getDfsPath() and getServerWithDfs()
SmbTree tree = null; // Initially null
String unc; // Initially null; set by getUncPath; never ends with '/'
int fid; // Initially 0; set by open()
int type;
boolean opened;
private int tree_num;
private CIFSContext transportContext;
private boolean nonPooled;
private SmbTransport exclusiveTransport;
private Random random = new Random();
/**
* Constructs an SmbFile representing a resource on an SMB network such as
* a file or directory. See the description and examples of smb URLs above.
*
* @param url
* A URL string
* @throws MalformedURLException
* If the parent
and child
parameters
* do not follow the prescribed syntax
*/
@Deprecated
public SmbFile ( String url ) throws MalformedURLException {
this(new URL(null, url, SingletonContext.getInstance().getUrlHandler()));
}
/**
* Constructs an SmbFile representing a resource on an SMB network such
* as a file or directory from a URL object.
*
* @param url
* The URL of the target resource
*/
@Deprecated
public SmbFile ( URL url ) {
this(url, SingletonContext.getInstance().withCredentials(new NtlmPasswordAuthentication(SingletonContext.getInstance(), url.getUserInfo())));
}
/**
* Constructs an SmbFile representing a resource on an SMB network such
* as a file or directory. The second parameter is a relative path from
* the parent SmbFile
. See the description above for examples
* of using the second name
parameter.
*
* @param context
* A base SmbFile
* @param name
* A path string relative to the parent
paremeter
* @throws MalformedURLException
* If the parent
and child
parameters
* do not follow the prescribed syntax
* @throws UnknownHostException
* If the server or workgroup of the context file cannot be determined
*/
public SmbFile ( SmbFile context, String name ) throws MalformedURLException, UnknownHostException {
this(
context.isWorkgroup0() ? new URL(null, "smb://" + checkName(name), context.transportContext.getUrlHandler())
: new URL(context.getURL(), checkName(name), context.transportContext.getUrlHandler()),
context.transportContext);
setupContext(context, name);
}
/**
* Constructs an SmbFile representing a resource on an SMB network such
* as a file or directory. The second parameter is a relative path from
* the context
. See the description above for examples of
* using the second name
parameter. The shareAccess
* parameter controls what permissions other clients have when trying
* to access the same file while this instance is still open. This
* value is either FILE_NO_SHARE or any combination
* of FILE_SHARE_READ, FILE_SHARE_WRITE, and
* FILE_SHARE_DELETE logically OR'd together.
*
* @param context
* A base SmbFile
* @param name
* A path string relative to the context
file path
* @param shareAccess
* Specifies what access other clients have while this file is open.
* @throws MalformedURLException
* If the context
and name
parameters
* do not follow the prescribed syntax
* @throws UnknownHostException
*/
public SmbFile ( SmbFile context, String name, int shareAccess ) throws MalformedURLException, UnknownHostException {
this(
context.isWorkgroup0() ? new URL(null, "smb://" + checkName(name), context.getTransportContext().getUrlHandler())
: new URL(context.getURL(), checkName(name), context.getTransportContext().getUrlHandler()),
context.transportContext);
if ( ( shareAccess & ~ ( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ) ) != 0 ) {
throw new RuntimeCIFSException("Illegal shareAccess parameter");
}
this.shareAccess = shareAccess;
setupContext(context, name);
}
/**
* @param url
* @param tc
* context to use
* @param shareAccess
* Specifies what access other clients have while this file is open.
* @throws MalformedURLException
*/
public SmbFile ( String url, CIFSContext tc, int shareAccess ) throws MalformedURLException {
this(new URL(null, url, tc.getUrlHandler()), tc);
if ( ( shareAccess & ~ ( FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE ) ) != 0 ) {
throw new RuntimeCIFSException("Illegal shareAccess parameter");
}
this.shareAccess = shareAccess;
}
/**
* Construct from string URL
*
* @param url
* @param tc
* context to use
* @throws MalformedURLException
*/
public SmbFile ( String url, CIFSContext tc ) throws MalformedURLException {
this(new URL(null, url, tc.getUrlHandler()), tc);
}
/**
* Construct from URL
*
* @param url
* @param tc
* context to use
*/
public SmbFile ( URL url, CIFSContext tc ) {
super(url);
this.transportContext = tc;
}
SmbFile ( SmbFile context, String name, boolean loadedAttributes, int type, int attributes, long createTime, long lastModified, long lastAccess,
long size ) throws MalformedURLException, UnknownHostException {
this(
context.isWorkgroup0() ? new URL(null, "smb://" + checkName(name) + "/", Handler.SMB_HANDLER)
: new URL(context.url, checkName(name) + ( ( attributes & ATTR_DIRECTORY ) > 0 ? "/" : "" )),
context.transportContext);
setupContext(context, name);
/*
* why? am I going around in circles?
* this.type = type == TYPE_WORKGROUP ? 0 : type;
*/
this.type = type;
this.attributes = attributes;
this.createTime = createTime;
this.lastModified = lastModified;
this.lastAccess = lastAccess;
this.size = size;
this.isExists = true;
if ( loadedAttributes ) {
this.attrExpiration = this.sizeExpiration = System.currentTimeMillis() + getTransportContext().getConfig().getAttributeCacheTimeout();
}
}
private static String checkName ( String name ) throws MalformedURLException {
if ( name == null || name.length() == 0 ) {
throw new MalformedURLException("Name must not be empty");
}
return name;
}
/**
* @param context
* @param name
*/
private final void setupContext ( SmbFile context, String name ) {
if ( context.share != null ) {
this.tree = context.tree;
this.dfsReferral = context.dfsReferral;
}
int last = name.length() - 1;
if ( last >= 0 && name.charAt(last) == '/' ) {
name = name.substring(0, last);
}
context.getUncPath0();
if ( context.share == null ) {
this.unc = "\\";
this.canon = "/";
}
else if ( context.unc.equals("\\") ) {
this.unc = '\\' + name;
this.canon = '/' + name;
this.share = context.share;
}
else {
this.unc = context.unc + '\\' + name;
this.canon = context.canon + '/' + name;
this.share = context.share;
}
}
/**
* @param nonPooled
* the nonPooled to set
*/
public void setNonPooled ( boolean nonPooled ) {
this.nonPooled = nonPooled;
}
/**
* @return the transportContext
*/
public CIFSContext getTransportContext () {
return this.transportContext;
}
/**
* @return session that this file has been loaded through
*/
public SmbSession getSession () {
SmbTree t = this.tree;
if ( t != null ) {
return t.session;
}
return null;
}
private SmbComBlankResponse blank_resp () {
if ( this.blank_resp == null ) {
this.blank_resp = new SmbComBlankResponse(getSession().getConfig());
}
return this.blank_resp;
}
void resolveDfs ( ServerMessageBlock request ) throws SmbException {
if ( request instanceof SmbComClose )
return;
for ( int retries = 0; retries < 1 + this.getTransportContext().getConfig().getMaxRequestRetries(); retries++ ) {
try {
resolveDfs0(request);
return;
}
catch ( NullPointerException npe ) {
// Bug where transport or tconHostName is null indicates
// failed to clean up properly from dropped connection.
log.debug("resolveDfs", npe);
}
catch ( SmbException smbe ) {
// The connection may have been dropped?
if ( smbe.getNtStatus() != NtStatus.NT_STATUS_NOT_FOUND ) {
throw smbe;
}
log.debug("resolveDfs", smbe);
}
// If we get here, we apparently have a bad connection.
// Disconnect and try again.
if ( log.isDebugEnabled() )
log.debug("Retrying (" + retries + ") resolveDfs: " + request);
disconnect(true);
try {
Thread.sleep(500 + this.random.nextInt(5000));
}
catch ( InterruptedException e ) {
log.debug("resolveDfs", e);
}
}
}
private void resolveDfs0 ( ServerMessageBlock request ) throws SmbException {
connect0();
SmbSession session = getSession();
SmbTransport transport = session.getTransport();
CIFSContext tc = getTransportContext();
DfsReferral dr = tc.getDfs().resolve(tc, transport.tconHostName, this.tree.share, this.unc);
if ( dr != null ) {
if ( log.isDebugEnabled() ) {
log.debug("Info " + transport.tconHostName + "\\" + this.tree.share + this.unc + " -> " + dr);
}
String service = this.tree != null ? this.tree.service : null;
if ( request != null ) {
switch ( request.command ) {
case ServerMessageBlock.SMB_COM_TRANSACTION:
case ServerMessageBlock.SMB_COM_TRANSACTION2:
switch ( ( (SmbComTransaction) request ).subCommand & 0xFF ) {
case SmbComTransaction.TRANS2_GET_DFS_REFERRAL:
break;
default:
service = "A:";
}
break;
default:
service = "A:";
}
}
SmbException se = null;
DfsReferral start = dr;
do {
try {
if ( log.isTraceEnabled() ) {
log.trace("DFS redirect: " + dr);
}
UniAddress addr = tc.getNameServiceClient().getByName(dr.server);
SmbTransport trans = tc.getTransportPool().getSmbTransport(tc, addr, this.url.getPort(), false, shouldForceSigning(tc, dr.share));
synchronized ( trans ) {
/*
* This is a key point. This is where we set the "tree" of this file which
* is like changing the rug out from underneath our feet.
*/
/*
* Technically we should also try to authenticate here but that means doing the session setup
* and
* tree connect separately. For now a simple connect will at least tell us if the host is alive.
* That should be sufficient for 99% of the cases. We can revisit this again for 2.0.
*/
trans.connect();
this.tree = trans.getSmbSession(tc).getSmbTree(dr.share, service);
if ( dr != start && dr.key != null ) {
dr.map.put(dr.key, dr);
}
}
se = null;
break;
}
catch ( IOException ioe ) {
log.debug("Error checking dfs root", ioe);
if ( ioe instanceof SmbException ) {
se = (SmbException) ioe;
}
else {
se = new SmbException("Failed to connect to server " + dr.server, ioe);
}
}
dr = dr.next;
}
while ( dr != start );
if ( se != null )
throw se;
this.dfsReferral = dr;
if ( dr.pathConsumed < 0 ) {
log.warn("Path consumed out of range " + dr.pathConsumed);
dr.pathConsumed = 0;
}
else if ( dr.pathConsumed > this.unc.length() ) {
log.warn("Path consumed out of range " + dr.pathConsumed);
dr.pathConsumed = this.unc.length();
}
if ( log.isDebugEnabled() ) {
log.debug("UNC is '" + this.unc + "'");
log.debug("Consumed '" + this.unc.substring(0, dr.pathConsumed) + "'");
}
String dunc = this.unc.substring(dr.pathConsumed);
if ( log.isDebugEnabled() ) {
log.debug("Remaining '" + dunc + "'");
}
if ( dunc.equals("") )
dunc = "\\";
if ( !dr.path.equals("") )
dunc = "\\" + dr.path + dunc;
if ( dunc.charAt(0) != '\\' ) {
log.warn("No slash at start of remaining DFS path " + dunc);
}
this.unc = dunc;
if ( log.isDebugEnabled() ) {
log.debug("Final resolved " + transport.tconHostName + "\\" + this.tree.share + this.unc + " -> " + dr);
}
if ( request != null && request.path != null && request.path.endsWith("\\") && dunc.endsWith("\\") == false ) {
dunc += "\\";
}
if ( request != null ) {
request.path = dunc;
request.flags2 |= SmbConstants.FLAGS2_RESOLVE_PATHS_IN_DFS;
}
}
else if ( this.tree.inDomainDfs && ! ( request instanceof NtTransQuerySecurityDesc ) && ! ( request instanceof SmbComClose )
&& ! ( request instanceof SmbComFindClose2 ) ) {
throw new SmbException(NtStatus.NT_STATUS_NOT_FOUND, false);
}
else {
log.trace("Not in DFS");
if ( request != null )
request.flags2 &= ~SmbConstants.FLAGS2_RESOLVE_PATHS_IN_DFS;
}
}
private static boolean shouldForceSigning ( CIFSContext tc, String share ) {
return tc.getConfig().isIpcSigningEnforced() && !tc.getCredentials().isAnonymous() && isIPC(share);
}
/**
* @param service
* @param share
* @return whether this is a IPC
*/
private static boolean isIPC ( String share ) {
log.debug("Check " + share);
if ( share == null || "IPC$".equals(share) ) {
if ( log.isDebugEnabled() ) {
log.debug("Share is " + share + " enforcing signing");
}
return true;
}
return false;
}
synchronized void disconnect ( boolean inError ) {
SmbTransport transport = this.getSession().transport();
synchronized ( transport ) {
SmbTree t = this.tree;
if ( t != null ) {
t.treeDisconnect(inError);
t.session.logoff(inError);
this.tree = null;
}
}
}
void send ( ServerMessageBlock request, ServerMessageBlock response ) throws SmbException {
send(request, response, true);
}
void send ( ServerMessageBlock request, ServerMessageBlock response, boolean timeout ) throws SmbException {
String savedPath = ( request != null ) ? request.path : null;
for ( int retries = 1; retries < getTransportContext().getConfig().getMaxRequestRetries(); retries++ ) {
try {
send0(request, response, timeout);
return;
}
catch ( SmbException smbe ) {
if ( smbe.getNtStatus() != NtStatus.NT_STATUS_INVALID_PARAMETER ) {
throw smbe;
}
log.debug("send", smbe);
}
// If we get here, we got the 'The Parameter is incorrect' error.
// Disconnect and try again from scratch.
if ( log.isDebugEnabled() )
log.debug("Retrying (" + retries + ") send: " + request);
disconnect(true);
try {
Thread.sleep(500 + this.random.nextInt(5000));
}
catch ( InterruptedException e ) {
log.debug("send", e);
}
if ( request != null ) {
// resolveDfs() and tree.send() modify the request packet.
// I want to restore it before retrying. request.reset()
// restores almost everything that was modified, except the path.
request.reset();
request.path = savedPath;
}
if ( response != null )
response.reset();
connect0();
}
}
private void send0 ( ServerMessageBlock request, ServerMessageBlock response, boolean timeout ) throws SmbException, DfsReferral {
for ( ;; ) {
resolveDfs(request);
try {
this.tree.send(request, response, timeout);
break;
}
catch ( DfsReferral dre ) {
if ( dre.resolveHashes ) {
throw dre;
}
request.reset();
log.trace("send0", dre);
}
}
}
static String queryLookup ( String query, String param ) {
char in[] = query.toCharArray();
int i, ch, st, eq;
st = eq = 0;
for ( i = 0; i < in.length; i++ ) {
ch = in[ i ];
if ( ch == '&' ) {
if ( eq > st ) {
String p = new String(in, st, eq - st);
if ( p.equalsIgnoreCase(param) ) {
eq++;
return new String(in, eq, i - eq);
}
}
st = i + 1;
}
else if ( ch == '=' ) {
eq = i;
}
}
if ( eq > st ) {
String p = new String(in, st, eq - st);
if ( p.equalsIgnoreCase(param) ) {
eq++;
return new String(in, eq, in.length - eq);
}
}
return null;
}
UniAddress[] addresses;
int addressIndex;
UniAddress getAddress () throws UnknownHostException {
if ( this.addressIndex == 0 )
return getFirstAddress();
return this.addresses[ this.addressIndex - 1 ];
}
UniAddress getFirstAddress () throws UnknownHostException {
this.addressIndex = 0;
String host = this.url.getHost();
String path = this.url.getPath();
String query = this.url.getQuery();
if ( query != null ) {
String server = queryLookup(query, "server");
if ( server != null && server.length() > 0 ) {
this.addresses = new UniAddress[1];
this.addresses[ 0 ] = getTransportContext().getNameServiceClient().getByName(server);
return getNextAddress();
}
String address = queryLookup(query, "address");
if ( address != null && address.length() > 0 ) {
byte[] ip = java.net.InetAddress.getByName(address).getAddress();
this.addresses = new UniAddress[1];
this.addresses[ 0 ] = new UniAddress(java.net.InetAddress.getByAddress(host, ip));
return getNextAddress();
}
}
if ( host.length() == 0 ) {
try {
NbtAddress addr = getTransportContext().getNameServiceClient().getNbtByName(NbtAddress.MASTER_BROWSER_NAME, 0x01, null);
this.addresses = new UniAddress[1];
this.addresses[ 0 ] = getTransportContext().getNameServiceClient().getByName(addr.getHostAddress());
}
catch ( UnknownHostException uhe ) {
log.debug("Unknown host", uhe);
if ( getTransportContext().getConfig().getDefaultDomain() == null ) {
throw uhe;
}
this.addresses = getTransportContext().getNameServiceClient()
.getAllByName(getTransportContext().getConfig().getDefaultDomain(), true);
}
}
else if ( path.length() == 0 || path.equals("/") ) {
this.addresses = getTransportContext().getNameServiceClient().getAllByName(host, true);
}
else {
this.addresses = getTransportContext().getNameServiceClient().getAllByName(host, false);
}
return getNextAddress();
}
UniAddress getNextAddress () {
UniAddress addr = null;
if ( this.addressIndex < this.addresses.length )
addr = this.addresses[ this.addressIndex++ ];
return addr;
}
boolean hasNextAddress () {
return this.addressIndex < this.addresses.length;
}
void connect0 () throws SmbException {
try {
connect();
}
catch ( UnknownHostException uhe ) {
throw new SmbException("Failed to connect to server", uhe);
}
catch ( SmbException se ) {
throw se;
}
catch ( IOException ioe ) {
throw new SmbException("Failed to connect to server", ioe);
}
}
synchronized void doConnect () throws IOException {
SmbTransport trans;
UniAddress addr = getAddress();
CIFSContext ctx = getTransportContext();
SmbTree t;
if ( log.isDebugEnabled() ) {
log.debug("Tree is " + this.tree);
}
if ( this.tree != null ) {
trans = getSession().getTransport();
t = this.tree;
}
else if ( this.nonPooled ) {
if ( log.isDebugEnabled() ) {
log.debug("Using exclusive transport for " + this);
}
this.exclusiveTransport = ctx.getTransportPool()
.getSmbTransport(ctx, addr, this.url.getPort(), true, shouldForceSigning(ctx, this.share));
trans = this.exclusiveTransport;
trans.setDontTimeout(true);
t = trans.getSmbSession(ctx).getSmbTree(this.share, null);
}
else {
trans = ctx.getTransportPool().getSmbTransport(ctx, addr, this.url.getPort(), false, shouldForceSigning(ctx, this.share));
t = trans.getSmbSession(ctx).getSmbTree(this.share, null);
}
if ( log.isDebugEnabled() && ( trans.flags2 & SmbConstants.FLAGS2_SECURITY_SIGNATURES ) != 0 && !trans.server.signaturesRequired
&& !isIPC(this.share) && !ctx.getConfig().isSigningEnforced() ) {
log.debug("Signatures for file enabled but not required " + this);
}
String hostName = getServerWithDfs();
DfsReferral referral = ctx.getDfs().resolve(ctx, hostName, t.share, null);
t.inDomainDfs = referral != null;
if ( t.inDomainDfs ) {
// make sure transport is connected
trans.connect();
t.connectionState = 2;
}
try {
if ( log.isTraceEnabled() )
log.trace("doConnect: " + addr);
t.treeConnect(null, null);
}
catch ( SmbAuthException sae ) {
log.debug("Authentication failed", sae);
SmbSession ssn;
if ( this.share == null ) { // IPC$ - try "anonymous" credentials
ssn = trans.getSmbSession(ctx.withAnonymousCredentials());
t = ssn.getSmbTree(null, null);
t.treeConnect(null, null);
}
else if ( ctx.renewCredentials(this.url.toString(), sae) ) {
log.debug("Trying to renew credentials after auth error");
ssn = trans.getSmbSession(ctx);
t = ssn.getSmbTree(this.share, null);
t.inDomainDfs = referral != null;
if ( this.tree.inDomainDfs ) {
this.tree.connectionState = 2;
}
t.treeConnect(null, null);
}
else {
throw sae;
}
}
this.tree = t;
}
/**
* It is not necessary to call this method directly. This is the
* URLConnection implementation of connect().
*/
@Override
public synchronized void connect () throws IOException {
if ( isConnected() && getSession().getTransport().tconHostName == null ) {
/*
* Tree/session thinks it is connected but transport disconnected
* under it, reset tree to reflect the truth.
*/
log.debug("Disconnecting failed tree and session");
disconnect(true);
}
if ( isConnected() ) {
log.trace("Already connected");
return;
}
getUncPath0();
getFirstAddress();
for ( ;; ) {
try {
doConnect();
return;
}
catch ( SmbAuthException sae ) {
throw sae; // Prevents account lockout on servers with multiple IPs
}
catch ( SmbException se ) {
if ( getNextAddress() == null )
throw se;
log.debug("Connect failed", se);
}
}
}
synchronized boolean isConnected () {
return this.tree != null && this.tree.isConnected();
}
int open0 ( int flags, int access, int attrs, int options ) throws SmbException {
int f;
connect0();
if ( log.isDebugEnabled() )
log.debug("open0: " + this.unc);
/*
* NT Create AndX / Open AndX Request / Response
*/
if ( getSession().getTransport().hasCapability(SmbConstants.CAP_NT_SMBS) ) {
SmbComNTCreateAndXResponse response = new SmbComNTCreateAndXResponse(getSession().getConfig());
SmbComNTCreateAndX request = new SmbComNTCreateAndX(
getSession().getConfig(),
this.unc,
flags,
access,
this.shareAccess,
attrs,
options,
null);
if ( this instanceof SmbNamedPipe ) {
request.flags0 |= 0x16;
request.desiredAccess |= 0x20000;
response.isExtended = true;
}
send(request, response);
f = response.fid;
this.type = response.fileType;
this.createTime = response.creationTime;
this.lastModified = response.lastWriteTime;
this.lastAccess = response.lastAccessTime;
this.size = response.allocationSize;
this.attributes = response.extFileAttributes & ATTR_GET_MASK;
this.attrExpiration = System.currentTimeMillis() + getTransportContext().getConfig().getAttributeCacheTimeout();
this.isExists = true;
}
else {
SmbComOpenAndXResponse response = new SmbComOpenAndXResponse(getSession().getConfig());
send(new SmbComOpenAndX(getSession().getConfig(), this.unc, access, flags, null), response);
f = response.fid;
}
return f;
}
void open ( int flags, int access, int attrs, int options ) throws SmbException {
if ( isOpen() ) {
return;
}
this.fid = open0(flags, access, attrs, options);
this.opened = true;
this.tree_num = this.tree.tree_num;
}
boolean isOpen () {
return this.opened && isConnected() && this.tree_num == this.tree.tree_num;
}
void close ( int f, long lastWriteTime ) throws SmbException {
if ( log.isDebugEnabled() ) {
log.debug("close: " + f);
}
/*
* Close Request / Response
*/
try {
send(new SmbComClose(getSession().getConfig(), f, lastWriteTime), blank_resp());
}
finally {
if ( this.exclusiveTransport != null ) {
try {
log.debug("Disconnecting exclusive transport");
this.exclusiveTransport.doDisconnect(false);
}
catch ( IOException e ) {
throw new SmbException("Failed to close exclusive transport");
}
}
}
}
void close ( long lastWriteTime ) throws SmbException {
if ( isOpen() == false ) {
return;
}
close(this.fid, lastWriteTime);
this.opened = false;
}
/**
* Close the file handle
*
* @throws SmbException
*/
public void close () throws SmbException {
close(0L);
}
/**
* Returns the last component of the target URL. This will
* effectively be the name of the file or directory represented by this
* SmbFile
or in the case of URLs that only specify a server
* or workgroup, the server or workgroup will be returned. The name of
* the root URL smb://
is also smb://
. If this
* SmbFile refers to a workgroup, server, share, or directory,
* the name will include a trailing slash '/' so that composing new
* SmbFiles will maintain the trailing slash requirement.
*
* @return The last component of the URL associated with this SMB
* resource or smb://
if the resource is smb://
* itself.
*/
public String getName () {
getUncPath0();
if ( this.canon.length() > 1 ) {
int i = this.canon.length() - 2;
while ( this.canon.charAt(i) != '/' ) {
i--;
}
return this.canon.substring(i + 1);
}
else if ( this.share != null ) {
return this.share + '/';
}
else if ( this.url.getHost().length() > 0 ) {
return this.url.getHost() + '/';
}
else {
return "smb://";
}
}
/**
* Everything but the last component of the URL representing this SMB
* resource is effectivly it's parent. The root URL smb://
* does not have a parent. In this case smb://
is returned.
*
* @return The parent directory of this SMB resource or
* smb://
if the resource refers to the root of the URL
* hierarchy which incedentally is also smb://
.
*/
public String getParent () {
String str = this.url.getAuthority();
if ( str.length() > 0 ) {
StringBuffer sb = new StringBuffer("smb://");
sb.append(str);
getUncPath0();
if ( this.canon.length() > 1 ) {
sb.append(this.canon);
}
else {
sb.append('/');
}
str = sb.toString();
int i = str.length() - 2;
while ( str.charAt(i) != '/' ) {
i--;
}
return str.substring(0, i + 1);
}
return "smb://";
}
/**
* Returns the full uncanonicalized URL of this SMB resource. An
* SmbFile
constructed with the result of this method will
* result in an SmbFile
that is equal to the original.
*
* @return The uncanonicalized full URL of this SMB resource.
*/
public String getPath () {
return this.url.toString();
}
String getUncPath0 () {
if ( this.unc == null ) {
char[] in = this.url.getPath().toCharArray();
char[] out = new char[in.length];
int length = in.length, i, o, state;
/*
* The canonicalization routine
*/
state = 0;
o = 0;
for ( i = 0; i < length; i++ ) {
switch ( state ) {
case 0:
if ( in[ i ] != '/' ) {
return null;
}
out[ o++ ] = in[ i ];
state = 1;
break;
case 1:
if ( in[ i ] == '/' ) {
break;
}
else if ( in[ i ] == '.' && ( ( i + 1 ) >= length || in[ i + 1 ] == '/' ) ) {
i++;
break;
}
else if ( ( i + 1 ) < length && in[ i ] == '.' && in[ i + 1 ] == '.' && ( ( i + 2 ) >= length || in[ i + 2 ] == '/' ) ) {
i += 2;
if ( o == 1 )
break;
do {
o--;
}
while ( o > 1 && out[ o - 1 ] != '/' );
break;
}
state = 2;
case 2:
if ( in[ i ] == '/' ) {
state = 1;
}
out[ o++ ] = in[ i ];
break;
}
}
this.canon = new String(out, 0, o);
if ( o > 1 ) {
o--;
i = this.canon.indexOf('/', 1);
if ( i < 0 ) {
this.share = this.canon.substring(1);
this.unc = "\\";
}
else if ( i == o ) {
this.share = this.canon.substring(1, i);
this.unc = "\\";
}
else {
this.share = this.canon.substring(1, i);
this.unc = this.canon.substring(i, out[ o ] == '/' ? o : o + 1);
this.unc = this.unc.replace('/', '\\');
}
}
else {
this.share = null;
this.unc = "\\";
}
}
return this.unc;
}
/**
* Retuns the Windows UNC style path with backslashs intead of forward slashes.
*
* @return The UNC path.
*/
public String getUncPath () {
getUncPath0();
if ( this.share == null ) {
return "\\\\" + this.url.getHost();
}
return "\\\\" + this.url.getHost() + this.canon.replace('/', '\\');
}
/**
* Returns the full URL of this SMB resource with '.' and '..' components
* factored out. An SmbFile
constructed with the result of
* this method will result in an SmbFile
that is equal to
* the original.
*
* @return The canonicalized URL of this SMB resource.
*/
public String getCanonicalPath () {
String str = this.url.getAuthority();
getUncPath0();
if ( str.length() > 0 ) {
return "smb://" + this.url.getAuthority() + this.canon;
}
return "smb://";
}
/**
* Retrieves the share associated with this SMB resource. In
* the case of smb://
, smb://workgroup/
,
* and smb://server/
URLs which do not specify a share,
* null
will be returned.
*
* @return The share component or null
if there is no share
*/
public String getShare () {
return this.share;
}
String getServerWithDfs () {
if ( this.dfsReferral != null ) {
return this.dfsReferral.server;
}
return getServer();
}
/**
* Retrieve the hostname of the server for this SMB resource. If this
* SmbFile
references a workgroup, the name of the workgroup
* is returned. If this SmbFile
refers to the root of this
* SMB network hierarchy, null
is returned.
*
* @return The server or workgroup name or null
if this
* SmbFile
refers to the root smb://
resource.
*/
public String getServer () {
String str = this.url.getHost();
if ( str.length() == 0 ) {
return null;
}
return str;
}
/**
* Returns type of of object this SmbFile represents.
*
* @return TYPE_FILESYSTEM, TYPE_WORKGROUP, TYPE_SERVER, TYPE_SHARE,
* TYPE_PRINTER, TYPE_NAMED_PIPE, or TYPE_COMM.
* @throws SmbException
*/
public int getType () throws SmbException {
if ( this.type == 0 ) {
if ( getUncPath0().length() > 1 ) {
this.type = TYPE_FILESYSTEM;
}
else if ( this.share != null ) {
// treeConnect good enough to test service type
connect0();
if ( this.share.equals("IPC$") ) {
this.type = TYPE_NAMED_PIPE;
}
else if ( this.tree.service.equals("LPT1:") ) {
this.type = TYPE_PRINTER;
}
else if ( this.tree.service.equals("COMM") ) {
this.type = TYPE_COMM;
}
else {
this.type = TYPE_SHARE;
}
}
else if ( this.url.getAuthority() == null || this.url.getAuthority().length() == 0 ) {
this.type = TYPE_WORKGROUP;
}
else {
UniAddress addr;
try {
addr = getAddress();
}
catch ( UnknownHostException uhe ) {
throw new SmbException(this.url.toString(), uhe);
}
if ( addr.getAddress() instanceof NbtAddress ) {
int code = ( (NbtAddress) addr.getAddress() ).getNameType();
if ( code == 0x1d || code == 0x1b ) {
this.type = TYPE_WORKGROUP;
return this.type;
}
}
this.type = TYPE_SERVER;
}
}
return this.type;
}
boolean isWorkgroup0 () throws UnknownHostException {
if ( this.type == TYPE_WORKGROUP || this.url.getHost().length() == 0 ) {
this.type = TYPE_WORKGROUP;
return true;
}
getUncPath0();
if ( this.share == null ) {
UniAddress addr = getAddress();
if ( addr.getAddress() instanceof NbtAddress ) {
int code = ( (NbtAddress) addr.getAddress() ).getNameType();
if ( code == 0x1d || code == 0x1b ) {
this.type = TYPE_WORKGROUP;
return true;
}
}
this.type = TYPE_SERVER;
}
return false;
}
Info queryPath ( String path, int infoLevel ) throws SmbException {
connect0();
if ( log.isDebugEnabled() ) {
log.debug("queryPath: " + path);
}
/*
* normally we'd check the negotiatedCapabilities for CAP_NT_SMBS
* however I can't seem to get a good last modified time from
* SMB_COM_QUERY_INFORMATION so if NT_SMBs are requested
* by the server than in this case that's what it will get
* regardless of what jcifs.smb.client.useNTSmbs is set
* to(overrides negotiatedCapabilities).
*/
/*
* We really should do the referral before this in case
* the redirected target has different capabilities. But
* the way we have been doing that is to call exists() which
* calls this method so another technique will be necessary
* to support DFS referral _to_ Win95/98/ME.
*/
if ( getSession().getTransport().hasCapability(SmbConstants.CAP_NT_SMBS) ) {
/*
* Trans2 Query Path Information Request / Response
*/
Trans2QueryPathInformationResponse response = new Trans2QueryPathInformationResponse(getSession().getConfig(), infoLevel);
send(new Trans2QueryPathInformation(getSession().getConfig(), path, infoLevel), response);
if ( log.isDebugEnabled() ) {
log.debug("Path information " + response);
}
return response.info;
}
/*
* Query Information Request / Response
*/
SmbComQueryInformationResponse response = new SmbComQueryInformationResponse(
getSession().getConfig(),
getSession().getTransport().server.serverTimeZone * 1000 * 60L);
send(new SmbComQueryInformation(getSession().getConfig(), path), response);
if ( log.isDebugEnabled() ) {
log.debug("Legacy path information " + response);
}
return response;
}
/**
* Tests to see if the SMB resource exists. If the resource refers
* only to a server, this method determines if the server exists on the
* network and is advertising SMB services. If this resource refers to
* a workgroup, this method determines if the workgroup name is valid on
* the local SMB network. If this SmbFile
refers to the root
* smb://
resource true
is always returned. If
* this SmbFile
is a traditional file or directory, it will
* be queried for on the specified server as expected.
*
* @return true
if the resource exists or is alive or
* false
otherwise
* @throws SmbException
*/
public boolean exists () throws SmbException {
if ( this.attrExpiration > System.currentTimeMillis() ) {
log.trace("Using cached attributes");
return this.isExists;
}
getUncPath0();
this.attributes = ATTR_READONLY | ATTR_DIRECTORY;
this.createTime = 0L;
this.lastModified = 0L;
this.lastAccess = 0L;
this.isExists = false;
try {
if ( this.url.getHost().length() == 0 ) {}
else if ( this.share == null ) {
if ( getType() == TYPE_WORKGROUP ) {
getTransportContext().getNameServiceClient().getByName(this.url.getHost(), true);
}
else {
getTransportContext().getNameServiceClient().getByName(this.url.getHost()).getHostName();
}
}
else if ( getUncPath0().length() == 1 || this.share.equalsIgnoreCase("IPC$") ) {
connect0(); // treeConnect is good enough
}
else {
Info info = queryPath(getUncPath0(), Trans2QueryPathInformationResponse.SMB_QUERY_FILE_BASIC_INFO);
this.attributes = info.getAttributes();
this.createTime = info.getCreateTime();
this.lastModified = info.getLastWriteTime();
this.lastAccess = info.getLastAccessTime();
}
/*
* If any of the above fail, isExists will not be set true
*/
this.isExists = true;
}
catch ( UnknownHostException uhe ) {
log.debug("Unknown host", uhe);
}
catch ( SmbException se ) {
log.trace("exists:", se);
switch ( se.getNtStatus() ) {
case NtStatus.NT_STATUS_NO_SUCH_FILE:
case NtStatus.NT_STATUS_OBJECT_NAME_INVALID:
case NtStatus.NT_STATUS_OBJECT_NAME_NOT_FOUND:
case NtStatus.NT_STATUS_OBJECT_PATH_NOT_FOUND:
break;
default:
throw se;
}
}
this.attrExpiration = System.currentTimeMillis() + getTransportContext().getConfig().getAttributeCacheTimeout();
return this.isExists;
}
/**
* Watches a directory for changes
*
* Will block until the server returns a set of changes that match the given filter. The file will be automatically
* opened if it is not and should be closed with {@link #close()} when no longer
* needed.
*
* Closing the file should cancel a pending notify request, but that does not seem to work reliable in all
* implementations.
*
* Changes in between these calls (as long as the file is open) are buffered by the server, so iteratively calling
* this method should provide all changes (size of that buffer can be adjusted through
* {@link jcifs.Configuration#getNotifyBufferSize()}).
* If the server cannot fulfill the request because the changes did not fit the buffer
* it will return an empty list of changes.
*
*
* @param filter
* see constants in {@link FileNotifyInformation}
* @param recursive
* whether to also watch subdirectories
* @return list of changes since the last watch call, empty if there were more changes than the server could handle.
* @throws SmbException
*/
public List watch ( int filter, boolean recursive ) throws SmbException {
if ( filter == 0 ) {
throw new IllegalArgumentException("filter must not be 0");
}
if ( !isDirectory() ) {
throw new SmbException("Is not a directory");
}
connect0();
if ( !this.getSession().getTransport().hasCapability(SmbConstants.CAP_NT_SMBS) ) {
throw new SmbUnsupportedOperationException("Not supported without CAP_NT_SMBS");
}
boolean wasOpen = this.opened;
if ( !wasOpen ) {
open(O_RDONLY, READ_CONTROL, 0, 1);
if ( log.isDebugEnabled() ) {
log.debug("Opened " + this.fid);
}
}
/*
* NtTrans Notify Change Request / Response
*/
NtTransNotifyChange request = new NtTransNotifyChange(getSession().getConfig(), this.fid, filter, recursive);
NtTransNotifyChangeResponse response = new NtTransNotifyChangeResponse(getSession().getConfig());
if ( log.isTraceEnabled() ) {
log.trace("Sending NtTransNotifyChange for " + this.fid);
}
send(request, response, false);
if ( log.isTraceEnabled() ) {
log.trace("Returned from NtTransNotifyChange " + response.status);
}
if ( response.status == 0x0000010B ) {
wasOpen = false;
}
if ( response.status == 0x0000010C ) {
response.notifyInformation.clear();
}
return response.notifyInformation;
}
/**
* Tests to see if the file this SmbFile
represents can be
* read. Because any file, directory, or other resource can be read if it
* exists, this method simply calls the exists
method.
*
* @return true
if the file is read-only
* @throws SmbException
*/
public boolean canRead () throws SmbException {
if ( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for reading?
return true;
}
return exists(); // try opening and catch sharing violation?
}
/**
* Tests to see if the file this SmbFile
represents
* exists and is not marked read-only. By default, resources are
* considered to be read-only and therefore for smb://
,
* smb://workgroup/
, and smb://server/
resources
* will be read-only.
*
* @return true
if the resource exists is not marked
* read-only
* @throws SmbException
*/
public boolean canWrite () throws SmbException {
if ( getType() == TYPE_NAMED_PIPE ) { // try opening the pipe for writing?
return true;
}
return exists() && ( this.attributes & ATTR_READONLY ) == 0;
}
/**
* Tests to see if the file this SmbFile
represents is a directory.
*
* @return true
if this SmbFile
is a directory
* @throws SmbException
*/
public boolean isDirectory () throws SmbException {
if ( getUncPath0().length() == 1 ) {
return true;
}
if ( !exists() )
return false;
return ( this.attributes & ATTR_DIRECTORY ) == ATTR_DIRECTORY;
}
/**
* Tests to see if the file this SmbFile
represents is not a directory.
*
* @return true
if this SmbFile
is not a directory
* @throws SmbException
*/
public boolean isFile () throws SmbException {
if ( getUncPath0().length() == 1 ) {
return false;
}
exists();
return ( this.attributes & ATTR_DIRECTORY ) == 0;
}
/**
* Tests to see if the file this SmbFile represents is marked as
* hidden. This method will also return true for shares with names that
* end with '$' such as IPC$
or C$
.
*
* @return true
if the SmbFile
is marked as being hidden
* @throws SmbException
*/
public boolean isHidden () throws SmbException {
if ( this.share == null ) {
return false;
}
else if ( getUncPath0().length() == 1 ) {
if ( this.share.endsWith("$") ) {
return true;
}
return false;
}
exists();
return ( this.attributes & ATTR_HIDDEN ) == ATTR_HIDDEN;
}
/**
* If the path of this SmbFile
falls within a DFS volume,
* this method will return the referral path to which it maps. Otherwise
* null
is returned.
*
* @return URL to the DFS volume
* @throws SmbException
*/
public String getDfsPath () throws SmbException {
resolveDfs(null);
if ( this.dfsReferral == null ) {
return null;
}
String path = "smb:/" + this.dfsReferral.server + "/" + this.dfsReferral.share + this.unc;
path = path.replace('\\', '/');
if ( isDirectory() ) {
path += '/';
}
return path;
}
/**
* Retrieve the time this SmbFile
was created. The value
* returned is suitable for constructing a {@link java.util.Date} object
* (i.e. seconds since Epoch 1970). Times should be the same as those
* reported using the properties dialog of the Windows Explorer program.
*
* For Win95/98/Me this is actually the last write time. It is currently
* not possible to retrieve the create time from files on these systems.
*
* @return The number of milliseconds since the 00:00:00 GMT, January 1,
* 1970 as a long
value
* @throws SmbException
*/
public long createTime () throws SmbException {
if ( getUncPath0().length() > 1 ) {
exists();
return this.createTime;
}
return 0L;
}
/**
* Retrieve the last time the file represented by this
* SmbFile
was modified. The value returned is suitable for
* constructing a {@link java.util.Date} object (i.e. seconds since Epoch
* 1970). Times should be the same as those reported using the properties
* dialog of the Windows Explorer program.
*
* @return The number of milliseconds since the 00:00:00 GMT, January 1,
* 1970 as a long
value
* @throws SmbException
*/
public long lastModified () throws SmbException {
if ( getUncPath0().length() > 1 ) {
exists();
return this.lastModified;
}
return 0L;
}
/**
* Retrieve the last acces time of the file represented by this SmbFile
*
* @return The number of milliseconds since the 00:00:00 GMT, January 1,
* 1970 as a long
value
* @throws SmbException
*/
public long lastAccess () throws SmbException {
if ( getUncPath0().length() > 1 ) {
exists();
return this.lastAccess;
}
return 0L;
}
/**
* List the contents of this SMB resource. The list returned by this
* method will be;
*
*
* - files and directories contained within this resource if the
* resource is a normal disk file directory,
*
- all available NetBIOS workgroups or domains if this resource is
* the top level URL
smb://
,
* - all servers registered as members of a NetBIOS workgroup if this
* resource refers to a workgroup in a
smb://workgroup/
URL,
* - all browseable shares of a server including printers, IPC
* services, or disk volumes if this resource is a server URL in the form
*
smb://server/
,
* - or
null
if the resource cannot be resolved.
*
*
* @return A String[]
array of files and directories,
* workgroups, servers, or shares depending on the context of the
* resource URL
* @throws SmbException
*/
public String[] list () throws SmbException {
return list("*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null);
}
/**
* List the contents of this SMB resource. The list returned will be
* identical to the list returned by the parameterless list()
* method minus filenames filtered by the specified filter.
*
* @param filter
* a filename filter to exclude filenames from the results
* @return String[]
array of matching files and directories,
* workgroups, servers, or shares depending on the context of the
* resource URL
* @throws SmbException
* # @return An array of filenames
*/
public String[] list ( SmbFilenameFilter filter ) throws SmbException {
return list("*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null);
}
/**
* List the contents of this SMB resource as an array of
* SmbFile
objects. This method is much more efficient than
* the regular list
method when querying attributes of each
* file in the result set.
*
* The list of SmbFile
s returned by this method will be;
*
*
* - files and directories contained within this resource if the
* resource is a normal disk file directory,
*
- all available NetBIOS workgroups or domains if this resource is
* the top level URL
smb://
,
* - all servers registered as members of a NetBIOS workgroup if this
* resource refers to a workgroup in a
smb://workgroup/
URL,
* - all browseable shares of a server including printers, IPC
* services, or disk volumes if this resource is a server URL in the form
*
smb://server/
,
* - or
null
if the resource cannot be resolved.
*
*
* @return An array of SmbFile
objects representing file
* and directories, workgroups, servers, or shares depending on the context
* of the resource URL
* @throws SmbException
*/
public SmbFile[] listFiles () throws SmbException {
return listFiles("*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null);
}
/**
* The CIFS protocol provides for DOS "wildcards" to be used as
* a performance enhancement. The client does not have to filter
* the names and the server does not have to return all directory
* entries.
*
* The wildcard expression may consist of two special meta
* characters in addition to the normal filename characters. The '*'
* character matches any number of characters in part of a name. If
* the expression begins with one or more '?'s then exactly that
* many characters will be matched whereas if it ends with '?'s
* it will match that many characters or less.
*
* Wildcard expressions will not filter workgroup names or server names.
*
*
*
*
* winnt> ls c?o*
* clock.avi -rw-- 82944 Mon Oct 14 1996 1:38 AM
* Cookies drw-- 0 Fri Nov 13 1998 9:42 PM
* 2 items in 5ms
*
*
*
*
* @param wildcard
* a wildcard expression
* @throws SmbException
* @return An array of SmbFile
objects representing file
* and directories, workgroups, servers, or shares depending on the context
* of the resource URL
*/
public SmbFile[] listFiles ( String wildcard ) throws SmbException {
return listFiles(wildcard, ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, null);
}
/**
* List the contents of this SMB resource. The list returned will be
* identical to the list returned by the parameterless listFiles()
* method minus files filtered by the specified filename filter.
*
* @param filter
* a filter to exclude files from the results
* @return An array of SmbFile objects
* @throws SmbException
*/
public SmbFile[] listFiles ( SmbFilenameFilter filter ) throws SmbException {
return listFiles("*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, filter, null);
}
/**
* List the contents of this SMB resource. The list returned will be
* identical to the list returned by the parameterless listFiles()
* method minus filenames filtered by the specified filter.
*
* @param filter
* a file filter to exclude files from the results
* @return An array of SmbFile objects
* @throws SmbException
*/
public SmbFile[] listFiles ( SmbFileFilter filter ) throws SmbException {
return listFiles("*", ATTR_DIRECTORY | ATTR_HIDDEN | ATTR_SYSTEM, null, filter);
}
String[] list ( String wildcard, int searchAttributes, SmbFilenameFilter fnf, SmbFileFilter ff ) throws SmbException {
List