Skip to content
This repository was archived by the owner on Jul 19, 2024. It is now read-only.

Commit 62d6e56

Browse files
author
jofriedm-msft
authored
Merge pull request #169 from jofriedm-msft/dev
Fix transactional MD5 validations on recovery
2 parents b31dcab + 1c67198 commit 62d6e56

File tree

8 files changed

+90
-21
lines changed

8 files changed

+90
-21
lines changed

ChangeLog.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2017.XX.XX Version X.X.X
2+
* Fixed a bug where the tranactional MD5 check would fail when downloading a range of blob or file and the recovery action is performed on a subsection of the range.
3+
14
2017.05.23 Version 5.2.0
25
* Fixed Exists() calls on Shares and Directories to now populate metadata. This was already being done for Files.
36
* Changed blob constants to support up to 256 MB on put blob for block blobs. The default value for put blob threshold has also been updated to half of the maximum, or 128 MB currently.

microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobEncryptStream.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,7 @@ public void write(byte[] data, int offset, int length) throws IOException {
145145

146146
@Override
147147
public void write(InputStream sourceStream, long writeLength) throws IOException, StorageException {
148-
Utility.writeToOutputStream(sourceStream, this, writeLength, false, false, this.opContext, this.options, false);
148+
Utility.writeToOutputStream(sourceStream, this, writeLength, false, false, this.opContext, this.options, false, null /* descriptor */);
149149
}
150150

151151
@Override

microsoft-azure-storage/src/com/microsoft/azure/storage/blob/BlobOutputStreamInternal.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -641,7 +641,7 @@ public void write(final byte[] data, final int offset, final int length) throws
641641
*/
642642
@DoesServiceRequest
643643
public void write(final InputStream sourceStream, final long writeLength) throws IOException, StorageException {
644-
Utility.writeToOutputStream(sourceStream, this, writeLength, false, false, this.opContext, this.options, false);
644+
Utility.writeToOutputStream(sourceStream, this, writeLength, false, false, this.opContext, this.options, false, null /* descriptor */);
645645
}
646646

647647
/**

microsoft-azure-storage/src/com/microsoft/azure/storage/blob/CloudBlob.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1402,7 +1402,7 @@ public Integer postProcessResponse(HttpURLConnection connection, CloudBlob blob,
14021402
// writeToOutputStream will update the currentRequestByteCount on this request in case a retry
14031403
// is needed and download should resume from that point
14041404
final StreamMd5AndLength descriptor = Utility.writeToOutputStream(streamRef, outStream, -1, false,
1405-
validateMD5, context, options, true, this);
1405+
validateMD5, context, options, true, this, this.getCurrentDescriptor());
14061406

14071407
// length was already checked by the NetworkInputStream, now check Md5
14081408
if (validateMD5 && !this.getContentMD5().equals(descriptor.getMd5())) {

microsoft-azure-storage/src/com/microsoft/azure/storage/core/StorageRequest.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ public abstract class StorageRequest<C, P, R> {
9999
*/
100100
private String contentMD5 = null;
101101

102+
/**
103+
* Holds the descriptor which contains the stream length and MD5 hash.
104+
*/
105+
private StreamMd5AndLength currentDescriptor = null;
106+
102107
/**
103108
* Denotes the StorageUri of the request
104109
*/
@@ -212,6 +217,11 @@ public final String getContentMD5() {
212217
return this.contentMD5;
213218
}
214219

220+
/**
221+
* @return the current descriptor which contains the stream length and MD5 hash.
222+
*/
223+
protected StreamMd5AndLength getCurrentDescriptor() { return this.currentDescriptor; }
224+
215225
/**
216226
* @return the location mode used to decide which location the request should be sent to.
217227
*/
@@ -457,6 +467,14 @@ public void setContentMD5(String contentMD5) {
457467
this.contentMD5 = contentMD5;
458468
}
459469

470+
/**
471+
* @param currentDescriptor
472+
* the descriptor value
473+
*/
474+
protected void setCurrentDescriptor(StreamMd5AndLength currentDescriptor) {
475+
this.currentDescriptor = currentDescriptor;
476+
}
477+
460478
/**
461479
* @param etagLockCondition
462480
* the locked ETag condition

microsoft-azure-storage/src/com/microsoft/azure/storage/core/Utility.java

Lines changed: 64 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,45 @@ public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStr
12721272
long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext,
12731273
final RequestOptions options, final Boolean shouldFlush) throws IOException, StorageException {
12741274
return writeToOutputStream(sourceStream, outStream, writeLength, rewindSourceStream, calculateMD5, opContext,
1275-
options, shouldFlush, null /*StorageRequest*/);
1275+
options, shouldFlush, null /*StorageRequest*/, null /* descriptor */);
1276+
}
1277+
1278+
/**
1279+
* Reads data from an input stream and writes it to an output stream, calculates the length of the data written, and
1280+
* optionally calculates the MD5 hash for the data.
1281+
*
1282+
* @param sourceStream
1283+
* An <code>InputStream</code> object that represents the input stream to use as the source.
1284+
* @param outStream
1285+
* An <code>OutputStream</code> object that represents the output stream to use as the destination.
1286+
* @param writeLength
1287+
* The number of bytes to read from the stream.
1288+
* @param rewindSourceStream
1289+
* <code>true</code> if the input stream should be rewound <strong>before</strong> it is read; otherwise,
1290+
* <code>false</code>
1291+
* @param calculateMD5
1292+
* <code>true</code> if an MD5 hash will be calculated; otherwise, <code>false</code>.
1293+
* @param opContext
1294+
* An {@link OperationContext} object that represents the context for the current operation. This object
1295+
* is used to track requests to the storage service, and to provide additional runtime information about
1296+
* the operation.
1297+
* @param options
1298+
* A {@link RequestOptions} object that specifies any additional options for the request. Namely, the
1299+
* maximum execution time.
1300+
* @param request
1301+
* Used by download resume to set currentRequestByteCount on the request. Otherwise, null is always used.
1302+
* @return A {@link StreamMd5AndLength} object that contains the output stream length, and optionally the MD5 hash.
1303+
*
1304+
* @throws IOException
1305+
* If an I/O error occurs.
1306+
* @throws StorageException
1307+
* If a storage service error occurred.
1308+
*/
1309+
public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStream, final OutputStream outStream,
1310+
long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext,
1311+
final RequestOptions options, final Boolean shouldFlush, StorageRequest<?, ?, Integer> request)
1312+
throws IOException, StorageException {
1313+
return writeToOutputStream(sourceStream, outStream, writeLength, rewindSourceStream, calculateMD5, opContext, options, shouldFlush, request, null /* descriptor */);
12761314
}
12771315

12781316
/**
@@ -1299,6 +1337,11 @@ public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStr
12991337
* maximum execution time.
13001338
* @param request
13011339
* Used by download resume to set currentRequestByteCount on the request. Otherwise, null is always used.
1340+
* @param descriptor
1341+
* A {@Link StreamMd5AndLength} object to append to in the case of recovery action or null if this is not called
1342+
* from a recovery. This value needs to be passed for recovery in case part of the body has already been read,
1343+
* the recovery will attempt to download the remaining bytes but will do MD5 validation on the originally
1344+
* requested range size.
13021345
* @return A {@link StreamMd5AndLength} object that contains the output stream length, and optionally the MD5 hash.
13031346
*
13041347
* @throws IOException
@@ -1308,24 +1351,28 @@ public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStr
13081351
*/
13091352
public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStream, final OutputStream outStream,
13101353
long writeLength, final boolean rewindSourceStream, final boolean calculateMD5, OperationContext opContext,
1311-
final RequestOptions options, final Boolean shouldFlush, StorageRequest<?, ?, Integer> request)
1354+
final RequestOptions options, final Boolean shouldFlush, StorageRequest<?, ?, Integer> request, StreamMd5AndLength descriptor)
13121355
throws IOException, StorageException {
13131356
if (rewindSourceStream && sourceStream.markSupported()) {
13141357
sourceStream.reset();
13151358
sourceStream.mark(Constants.MAX_MARK_LENGTH);
13161359
}
13171360

1318-
final StreamMd5AndLength retVal = new StreamMd5AndLength();
1319-
1320-
if (calculateMD5) {
1321-
try {
1322-
retVal.setDigest(MessageDigest.getInstance("MD5"));
1323-
}
1324-
catch (final NoSuchAlgorithmException e) {
1325-
// This wont happen, throw fatal.
1326-
throw Utility.generateNewUnexpectedStorageException(e);
1361+
if (descriptor == null) {
1362+
descriptor = new StreamMd5AndLength();
1363+
if (calculateMD5) {
1364+
try {
1365+
descriptor.setDigest(MessageDigest.getInstance("MD5"));
1366+
}
1367+
catch (final NoSuchAlgorithmException e) {
1368+
// This wont happen, throw fatal.
1369+
throw Utility.generateNewUnexpectedStorageException(e);
1370+
}
13271371
}
13281372
}
1373+
else {
1374+
descriptor.setMd5(null);
1375+
}
13291376

13301377
if (writeLength < 0) {
13311378
writeLength = Long.MAX_VALUE;
@@ -1349,25 +1396,26 @@ public static StreamMd5AndLength writeToOutputStream(final InputStream sourceStr
13491396
}
13501397

13511398
if (calculateMD5) {
1352-
retVal.getDigest().update(retrievedBuff, 0, count);
1399+
descriptor.getDigest().update(retrievedBuff, 0, count);
13531400
}
13541401

1355-
retVal.setLength(retVal.getLength() + count);
1356-
retVal.setCurrentOperationByteCount(retVal.getCurrentOperationByteCount() + count);
1402+
descriptor.setLength(descriptor.getLength() + count);
1403+
descriptor.setCurrentOperationByteCount(descriptor.getCurrentOperationByteCount() + count);
13571404

13581405
if (request != null) {
13591406
request.setCurrentRequestByteCount(request.getCurrentRequestByteCount() + count);
1407+
request.setCurrentDescriptor(descriptor);
13601408
}
13611409

1362-
nextCopy = (int) Math.min(retrievedBuff.length, writeLength - retVal.getLength());
1410+
nextCopy = (int) Math.min(retrievedBuff.length, writeLength - descriptor.getLength());
13631411
count = sourceStream.read(retrievedBuff, 0, nextCopy);
13641412
}
13651413

13661414
if (outStream != null && shouldFlush) {
13671415
outStream.flush();
13681416
}
13691417

1370-
return retVal;
1418+
return descriptor;
13711419
}
13721420

13731421
/**

microsoft-azure-storage/src/com/microsoft/azure/storage/file/CloudFile.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1411,7 +1411,7 @@ public Integer postProcessResponse(HttpURLConnection connection, CloudFile file,
14111411
// writeToOutputStream will update the currentRequestByteCount on this request in case a retry
14121412
// is needed and download should resume from that point
14131413
final StreamMd5AndLength descriptor = Utility.writeToOutputStream(streamRef, outStream, -1, false,
1414-
validateMD5, context, options, true, this);
1414+
validateMD5, context, options, true, this, this.getCurrentDescriptor());
14151415

14161416
// length was already checked by the NetworkInputStream, now check Md5
14171417
if (validateMD5 && !this.getContentMD5().equals(descriptor.getMd5())) {

microsoft-azure-storage/src/com/microsoft/azure/storage/file/FileOutputStream.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ public void write(final byte[] data, final int offset, final int length) throws
404404
*/
405405
@DoesServiceRequest
406406
public void write(final InputStream sourceStream, final long writeLength) throws IOException, StorageException {
407-
Utility.writeToOutputStream(sourceStream, this, writeLength, false, false, this.opContext, this.options, false);
407+
Utility.writeToOutputStream(sourceStream, this, writeLength, false, false, this.opContext, this.options, false, null /* descriptor */);
408408
}
409409

410410
/**

0 commit comments

Comments
 (0)