jcifs.smb.SmbCopyUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcifs-ng Show documentation
Show all versions of jcifs-ng Show documentation
A pure-java CIFS/SMB client library
/*
* © 2017 AgNO3 Gmbh & Co. KG
*
* 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.net.MalformedURLException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jcifs.CIFSException;
import jcifs.CloseableIterator;
import jcifs.SmbConstants;
import jcifs.SmbResource;
import jcifs.internal.fscc.FileBasicInfo;
import jcifs.internal.smb1.com.SmbComSetInformation;
import jcifs.internal.smb1.com.SmbComSetInformationResponse;
import jcifs.internal.smb1.trans2.Trans2SetFileInformation;
import jcifs.internal.smb1.trans2.Trans2SetFileInformationResponse;
import jcifs.internal.smb2.info.Smb2SetInfoRequest;
import jcifs.internal.smb2.ioctl.Smb2IoctlRequest;
import jcifs.internal.smb2.ioctl.Smb2IoctlResponse;
import jcifs.internal.smb2.ioctl.SrvCopyChunkCopyResponse;
import jcifs.internal.smb2.ioctl.SrvCopychunk;
import jcifs.internal.smb2.ioctl.SrvCopychunkCopy;
import jcifs.internal.smb2.ioctl.SrvRequestResumeKeyResponse;
/**
* @author mbechler
*
*/
final class SmbCopyUtil {
private static final Logger log = LoggerFactory.getLogger(SmbCopyUtil.class);
/**
*
*/
private SmbCopyUtil () {}
/**
* @param dest
* @return
* @throws SmbException
* @throws SmbAuthException
*/
static SmbFileHandleImpl openCopyTargetFile ( SmbFile dest, int attrs, boolean alsoRead ) throws CIFSException {
try {
return dest.openUnshared(
SmbConstants.O_CREAT | SmbConstants.O_WRONLY | SmbConstants.O_TRUNC,
SmbConstants.FILE_WRITE_DATA | SmbConstants.FILE_WRITE_ATTRIBUTES | ( alsoRead ? SmbConstants.FILE_READ_DATA : 0 ),
SmbConstants.FILE_NO_SHARE,
attrs,
0);
}
catch ( SmbAuthException sae ) {
log.trace("copyTo0", sae);
int dattrs = dest.getAttributes();
if ( ( dattrs & SmbConstants.ATTR_READONLY ) != 0 ) {
/*
* Remove READONLY and try again
*/
dest.setPathInformation(dattrs & ~SmbConstants.ATTR_READONLY, 0L, 0L, 0L);
return dest.openUnshared(
SmbConstants.O_CREAT | SmbConstants.O_WRONLY | SmbConstants.O_TRUNC,
SmbConstants.FILE_WRITE_DATA | SmbConstants.FILE_WRITE_ATTRIBUTES | ( alsoRead ? SmbConstants.FILE_READ_DATA : 0 ),
SmbConstants.FILE_NO_SHARE,
attrs,
0);
}
throw sae;
}
}
/**
* @param dest
* @param b
* @param bsize
* @param w
* @param dh
* @param sh
* @param req
* @param resp
* @throws SmbException
*/
static void copyFile ( SmbFile src, SmbFile dest, byte[][] b, int bsize, WriterThread w, SmbTreeHandleImpl sh, SmbTreeHandleImpl dh )
throws SmbException {
if ( sh.isSMB2() && dh.isSMB2() && sh.isSameTree(dh) ) {
try {
serverSideCopy(src, dest, sh, dh, false);
return;
}
catch ( CIFSException e ) {
log.warn("Server side copy failed", e);
throw SmbException.wrap(e);
}
}
try ( SmbFileHandleImpl sfd = src.openUnshared(0, SmbConstants.O_RDONLY, SmbConstants.FILE_SHARE_READ, SmbConstants.ATTR_NORMAL, 0);
SmbFileInputStream fis = new SmbFileInputStream(src, sh, sfd) ) {
int attrs = src.getAttributes();
try ( SmbFileHandleImpl dfd = openCopyTargetFile(dest, attrs, false);
SmbFileOutputStream fos = new SmbFileOutputStream(
dest,
dh,
dfd,
SmbConstants.O_CREAT | SmbConstants.O_WRONLY | SmbConstants.O_TRUNC,
SmbConstants.FILE_WRITE_DATA | SmbConstants.FILE_WRITE_ATTRIBUTES,
SmbConstants.FILE_NO_SHARE) ) {
long mtime = src.lastModified();
long ctime = src.createTime();
long atime = src.lastAccess();
int i = 0;
long off = 0L;
while ( true ) {
int read = fis.read(b[ i ]);
synchronized ( w ) {
w.checkException();
while ( !w.isReady() ) {
try {
w.wait();
}
catch ( InterruptedException ie ) {
throw new SmbException(dest.getURL().toString(), ie);
}
}
w.checkException();
if ( read <= 0 ) {
break;
}
w.write(b[ i ], read, fos);
}
i = i == 1 ? 0 : 1;
off += read;
}
if ( log.isDebugEnabled() ) {
log.debug(String.format("Copied a total of %d bytes", off));
}
if ( dh.isSMB2() ) {
Smb2SetInfoRequest req = new Smb2SetInfoRequest(dh.getConfig(), dfd.getFileId());
req.setFileInformation(new FileBasicInfo(ctime, atime, mtime, 0L, attrs));
dh.send(req);
}
else if ( dh.hasCapability(SmbConstants.CAP_NT_SMBS) ) {
// use the open file descriptor
dh.send(
new Trans2SetFileInformation(dh.getConfig(), dfd.getFid(), attrs, ctime, mtime, atime),
new Trans2SetFileInformationResponse(dh.getConfig()));
}
else {
dh.send(
new SmbComSetInformation(dh.getConfig(), dest.getUncPath(), attrs, mtime),
new SmbComSetInformationResponse(dh.getConfig()));
}
}
}
catch ( IOException se ) {
if ( !src.getContext().getConfig().isIgnoreCopyToException() ) {
throw new SmbException("Failed to copy file from [" + src.toString() + "] to [" + dest.toString() + "]", se);
}
log.warn("Copy failed", se);
}
}
/**
* @param src
* @param dest
* @param sh
* @param dh
* @throws SmbException
*/
@SuppressWarnings ( "resource" )
private static void serverSideCopy ( SmbFile src, SmbFile dest, SmbTreeHandleImpl sh, SmbTreeHandleImpl dh, boolean write ) throws CIFSException {
log.debug("Trying server side copy");
SmbFileHandleImpl dfd = null;
try {
long size;
byte[] resumeKey;
// despite there being a resume key, we still need an open file descriptor?
try ( SmbFileHandleImpl sfd = src.openUnshared(0, SmbConstants.O_RDONLY, SmbConstants.FILE_SHARE_READ, SmbConstants.ATTR_NORMAL, 0) ) {
if ( sfd.getInitialSize() == 0 ) {
try ( SmbFileHandleImpl edfd = openCopyTargetFile(dest, src.getAttributes(), !write) ) {
return;
}
}
Smb2IoctlRequest resumeReq = new Smb2IoctlRequest(sh.getConfig(), Smb2IoctlRequest.FSCTL_SRV_REQUEST_RESUME_KEY, sfd.getFileId());
resumeReq.setFlags(Smb2IoctlRequest.SMB2_O_IOCTL_IS_FSCTL);
Smb2IoctlResponse resumeResp = sh.send(resumeReq);
SrvRequestResumeKeyResponse rkresp = resumeResp.getOutputData(SrvRequestResumeKeyResponse.class);
size = sfd.getInitialSize();
resumeKey = rkresp.getResumeKey();
// start with some reasonably safe defaults, the server will till us if it does not like it
// can we resume this if we loose the file descriptor?
int maxChunks = 256;
int maxChunkSize = 1024 * 1024;
int byteLimit = 16 * 1024 * 1024;
boolean retry = false;
do {
long ooff = 0;
while ( ooff < size ) {
long wsize = size - ooff;
if ( wsize > byteLimit ) {
wsize = byteLimit;
}
int chunks = (int) ( wsize / maxChunkSize );
int lastChunkSize;
if ( chunks + 1 > maxChunks ) {
chunks = maxChunks;
lastChunkSize = maxChunkSize;
}
else {
lastChunkSize = (int) ( wsize % maxChunkSize );
if ( lastChunkSize != 0 ) {
chunks++;
}
else {
lastChunkSize = maxChunkSize;
}
}
SrvCopychunk[] chunkInfo = new SrvCopychunk[chunks];
long ioff = 0;
for ( int i = 0; i < chunks; i++ ) {
long absoff = ooff + ioff;
int csize = i == chunks - 1 ? lastChunkSize : maxChunkSize;
chunkInfo[ i ] = new SrvCopychunk(absoff, absoff, csize);
ioff += maxChunkSize;
}
if ( dfd == null || !dfd.isValid() ) {
// don't reopen the file for every round if it's not necessary, keep the lock
dfd = openCopyTargetFile(dest, src.getAttributes(), !write);
}
// FSCTL_SRV_COPYCHUNK_WRITE allows to open the file for writing only, FSCTL_SRV_COPYCHUNK also
// needs read access
Smb2IoctlRequest copy = new Smb2IoctlRequest(
sh.getConfig(),
write ? Smb2IoctlRequest.FSCTL_SRV_COPYCHUNK_WRITE : Smb2IoctlRequest.FSCTL_SRV_COPYCHUNK,
dfd.getFileId());
copy.setFlags(Smb2IoctlRequest.SMB2_O_IOCTL_IS_FSCTL);
copy.setInputData(new SrvCopychunkCopy(resumeKey, chunkInfo));
try {
SrvCopyChunkCopyResponse r = dh.send(copy, RequestParam.NO_RETRY).getOutputData(SrvCopyChunkCopyResponse.class);
if ( log.isDebugEnabled() ) {
log.debug(
String.format(
"Wrote %d bytes (%d chunks, last partial write %d)",
r.getTotalBytesWritten(),
r.getChunksWritten(),
r.getChunkBytesWritten()));
}
ooff += r.getTotalBytesWritten();
}
catch ( SmbException e ) {
Smb2IoctlResponse response = copy.getResponse();
if ( !retry && response.isReceived() && !response.isError()
&& response.getStatus() == NtStatus.NT_STATUS_INVALID_PARAMETER ) {
retry = true;
SrvCopyChunkCopyResponse outputData = response.getOutputData(SrvCopyChunkCopyResponse.class);
maxChunks = outputData.getChunksWritten();
maxChunkSize = outputData.getChunkBytesWritten();
byteLimit = outputData.getTotalBytesWritten();
continue;
}
throw e;
}
}
break;
}
while ( retry );
}
}
catch ( IOException se ) {
throw new CIFSException("Server side copy failed", se);
}
finally {
if ( dfd != null ) {
dfd.close();
}
}
}
/**
* @param dest
* @param b
* @param bsize
* @param w
* @param dh
* @param sh
* @param req
* @param resp
* @throws SmbException
*/
static void copyDir ( SmbFile src, SmbFile dest, byte[][] b, int bsize, WriterThread w, SmbTreeHandleImpl sh, SmbTreeHandleImpl dh )
throws CIFSException {
String path = dest.getLocator().getUNCPath();
if ( path.length() > 1 ) {
try {
dest.mkdir();
if ( dh.hasCapability(SmbConstants.CAP_NT_SMBS) ) {
dest.setPathInformation(src.getAttributes(), src.createTime(), src.lastModified(), src.lastAccess());
}
else {
dest.setPathInformation(src.getAttributes(), 0L, src.lastModified(), 0L);
}
}
catch ( SmbException se ) {
log.trace("copyTo0", se);
if ( se.getNtStatus() != NtStatus.NT_STATUS_ACCESS_DENIED && se.getNtStatus() != NtStatus.NT_STATUS_OBJECT_NAME_COLLISION ) {
throw se;
}
}
}
try ( CloseableIterator it = SmbEnumerationUtil
.doEnum(src, "*", SmbConstants.ATTR_DIRECTORY | SmbConstants.ATTR_HIDDEN | SmbConstants.ATTR_SYSTEM, null, null) ) {
while ( it.hasNext() ) {
try ( SmbResource r = it.next() ) {
try ( SmbFile ndest = new SmbFile(
dest,
r.getLocator().getName(),
true,
r.getLocator().getType(),
r.getAttributes(),
r.createTime(),
r.lastModified(),
r.lastAccess(),
r.length()) ) {
if ( r instanceof SmbFile ) {
( (SmbFile) r ).copyRecursive(ndest, b, bsize, w, sh, dh);
}
}
}
}
}
catch ( MalformedURLException mue ) {
throw new SmbException(src.getURL().toString(), mue);
}
}
}
class WriterThread extends Thread {
private byte[] b;
private int n;
private boolean ready;
private SmbFileOutputStream out;
private SmbException e = null;
WriterThread () {
super("JCIFS-WriterThread");
this.ready = false;
}
/**
* @return the ready
*/
boolean isReady () {
return this.ready;
}
/**
* @throws SmbException
*
*/
public void checkException () throws SmbException {
if ( this.e != null ) {
throw this.e;
}
}
synchronized void write ( byte[] buffer, int len, SmbFileOutputStream d ) {
this.b = buffer;
this.n = len;
this.out = d;
this.ready = false;
notify();
}
@Override
public void run () {
synchronized ( this ) {
try {
for ( ;; ) {
notify();
this.ready = true;
while ( this.ready ) {
wait();
}
if ( this.n == -1 ) {
return;
}
this.out.write(this.b, 0, this.n);
}
}
catch ( SmbException ex ) {
this.e = ex;
}
catch ( Exception x ) {
this.e = new SmbException("WriterThread", x);
}
notify();
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy