in sshd-sftp/src/main/java/org/apache/sshd/sftp/server/AbstractSftpSubsystemHelper.java [931:1024]
protected void doCheckFileHash(
int id, Path file, NamedFactory<? extends Digest> factory,
long startOffset, long length, int blockSize, Buffer buffer)
throws Exception {
ValidateUtils.checkTrue(startOffset >= 0L, "Invalid start offset: %d", startOffset);
ValidateUtils.checkTrue(length >= 0L, "Invalid length: %d", length);
ValidateUtils.checkTrue(
(blockSize == 0) || (blockSize >= SftpConstants.MIN_CHKFILE_BLOCKSIZE),
"Invalid block size: %d", blockSize);
Objects.requireNonNull(factory, "No digest factory provided");
buffer.putString(factory.getName());
long effectiveLength = length;
long totalLength = Files.size(file);
if (effectiveLength == 0L) {
effectiveLength = totalLength - startOffset;
} else {
long maxRead = startOffset + length;
if (maxRead > totalLength) {
effectiveLength = totalLength - startOffset;
}
}
ValidateUtils.checkTrue(effectiveLength > 0L,
"Non-positive effective hash data length: %d", effectiveLength);
byte[] digestBuf = (blockSize == 0)
? new byte[Math.min((int) effectiveLength, IoUtils.DEFAULT_COPY_SIZE)]
: new byte[Math.min((int) effectiveLength, blockSize)];
ByteBuffer wb = ByteBuffer.wrap(digestBuf);
SftpFileSystemAccessor accessor = getFileSystemAccessor();
ServerSession session = getServerSession();
try (SeekableByteChannel channel = accessor.openFile(
this, null, file, null, Collections.emptySet())) {
channel.position(startOffset);
Digest digest = factory.create();
digest.init();
boolean traceEnabled = log.isTraceEnabled();
if (blockSize == 0) {
while (effectiveLength > 0L) {
int remainLen = Math.min(digestBuf.length, (int) effectiveLength);
ByteBuffer bb = wb;
if (remainLen < digestBuf.length) {
bb = ByteBuffer.wrap(digestBuf, 0, remainLen);
}
bb.clear(); // prepare for next read
int readLen = channel.read(bb);
if (readLen < 0) {
break;
}
effectiveLength -= readLen;
digest.update(digestBuf, 0, readLen);
}
byte[] hashValue = digest.digest();
if (traceEnabled) {
log.trace("doCheckFileHash({})[{}] offset={}, length={} - algo={}, hash={}",
session, file, startOffset, length,
digest.getAlgorithm(), BufferUtils.toHex(':', hashValue));
}
buffer.putBytes(hashValue);
} else {
for (int count = 0; effectiveLength > 0L; count++) {
int remainLen = Math.min(digestBuf.length, (int) effectiveLength);
ByteBuffer bb = wb;
if (remainLen < digestBuf.length) {
bb = ByteBuffer.wrap(digestBuf, 0, remainLen);
}
bb.clear(); // prepare for next read
int readLen = channel.read(bb);
if (readLen < 0) {
break;
}
effectiveLength -= readLen;
digest.update(digestBuf, 0, readLen);
byte[] hashValue = digest.digest(); // NOTE: this also resets the hash for the next read
if (traceEnabled) {
log.trace("doCheckFileHash({})({})[{}] offset={}, length={} - algo={}, hash={}",
session, file, count, startOffset, length,
digest.getAlgorithm(), BufferUtils.toHex(':', hashValue));
}
buffer.putBytes(hashValue);
}
}
accessor.closeFile(this, null, file, null, channel, Collections.emptySet());
}
}