/*
* Copyright 2016 The Simple File Server Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.sfs.nodes.compute.object;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.logging.Logger;
import io.vertx.core.streams.ReadStream;
import org.sfs.Server;
import org.sfs.VertxContext;
import org.sfs.encryption.ContainerKeys;
import org.sfs.filesystem.volume.DigestBlob;
import org.sfs.io.BufferWriteEndableWriteStream;
import org.sfs.io.CountingReadStream;
import org.sfs.io.DigestReadStream;
import org.sfs.nodes.Nodes;
import org.sfs.nodes.VolumeReplicaGroup;
import org.sfs.nodes.XNode;
import org.sfs.rx.Holder2;
import org.sfs.util.MessageDigestFactory;
import org.sfs.vo.PersistentContainer;
import org.sfs.vo.TransientSegment;
import org.sfs.vo.TransientVersion;
import rx.Observable;
import rx.functions.Func1;
import static io.vertx.core.logging.LoggerFactory.getLogger;
import static org.sfs.filesystem.volume.VolumeV1.TINY_DATA_THRESHOLD;
import static org.sfs.io.AsyncIO.pump;
import static org.sfs.util.MessageDigestFactory.MD5;
import static org.sfs.util.MessageDigestFactory.SHA512;
import static org.sfs.vo.Segment.SegmentCipher;
public class WriteNewSegment implements Func1<TransientVersion, Observable<TransientSegment>> {
private static final Logger LOGGER = getLogger(WriteNewSegment.class);
private final VertxContext<Server> vertxContext;
private final ReadStream<Buffer> readStream;
public WriteNewSegment(VertxContext<Server> vertxContext, ReadStream<Buffer> readStream) {
this.vertxContext = vertxContext;
this.readStream = readStream;
}
@Override
public Observable<TransientSegment> call(TransientVersion transientVersion) {
final PersistentContainer persistentContainer = transientVersion.getParent().getParent();
final long contentLength = transientVersion.getContentLength().get();
final boolean serverSideEncryption = transientVersion.useServerSideEncryption();
final Nodes nodes = vertxContext.verticle().nodes();
final MessageDigestFactory sha512Digest = SHA512;
final MessageDigestFactory md5Digest = MD5;
if (serverSideEncryption) {
ContainerKeys containerKeys = vertxContext.verticle().containerKeys();
return containerKeys.preferredAlgorithm(vertxContext, persistentContainer)
.flatMap(keyResponse -> {
VolumeReplicaGroup volumeReplicaGroup = new VolumeReplicaGroup(vertxContext, nodes.getNumberOfObjectCopies())
.setAllowSameNode(nodes.isAllowSameNode());
long encryptedLength = keyResponse.getData().encryptOutputSize(contentLength);
final CountingReadStream clearByteCount = new CountingReadStream(readStream);
final DigestReadStream serverObjectDigestReadStream = new DigestReadStream(clearByteCount, md5Digest, sha512Digest);
ReadStream<Buffer> cipherWriteStream = keyResponse.getData().encrypt(serverObjectDigestReadStream);
final CountingReadStream encryptedByteCount = new CountingReadStream(cipherWriteStream);
final DigestReadStream blobDigestReadStream = new DigestReadStream(encryptedByteCount, md5Digest, sha512Digest);
if (encryptedLength > TINY_DATA_THRESHOLD) {
return volumeReplicaGroup.consume(encryptedLength, sha512Digest, blobDigestReadStream)
.map(digestBlobs -> {
SegmentCipher segmentCipher = new SegmentCipher(keyResponse.getKeyId(), keyResponse.getSalt());
final TransientSegment newSegment = transientVersion.newSegment();
newSegment.setWriteSha512(blobDigestReadStream.getDigest(sha512Digest).get())
.setSegmentCipher(segmentCipher)
.setWriteLength(encryptedByteCount.count())
.setReadSha512(serverObjectDigestReadStream.getDigest(sha512Digest).get())
.setReadMd5(serverObjectDigestReadStream.getDigest(md5Digest).get())
.setReadLength(clearByteCount.count())
.setIsTinyData(false);
for (DigestBlob digestBlob : digestBlobs) {
newSegment.newBlob()
.setVolumeId(digestBlob.getVolume())
.setPosition(digestBlob.getPosition())
.setReadLength(digestBlob.getLength())
.setReadSha512(digestBlob.getDigest(sha512Digest).get());
}
return newSegment;
});
} else {
BufferWriteEndableWriteStream bufferWriteStream = new BufferWriteEndableWriteStream();
return pump(blobDigestReadStream, bufferWriteStream)
.map(aVoid -> {
SegmentCipher segmentCipher = new SegmentCipher(keyResponse.getKeyId(), keyResponse.getSalt());
final TransientSegment newSegment = transientVersion.newSegment();
newSegment.setWriteSha512(blobDigestReadStream.getDigest(sha512Digest).get())
.setSegmentCipher(segmentCipher)
.setWriteLength(encryptedByteCount.count())
.setReadSha512(serverObjectDigestReadStream.getDigest(sha512Digest).get())
.setReadMd5(serverObjectDigestReadStream.getDigest(md5Digest).get())
.setReadLength(clearByteCount.count())
.setIsTinyData(true)
.setTinyData(bufferWriteStream.toBuffer().getBytes());
return newSegment;
});
}
});
} else {
VolumeReplicaGroup volumeReplicaGroup =
new VolumeReplicaGroup(vertxContext, nodes.getNumberOfObjectCopies())
.setAllowSameNode(nodes.isAllowSameNode());
final CountingReadStream clearByteCount = new CountingReadStream(readStream);
final DigestReadStream digestReadStream = new DigestReadStream(clearByteCount, md5Digest, sha512Digest);
if (contentLength > TINY_DATA_THRESHOLD) {
return volumeReplicaGroup.consume(contentLength, sha512Digest, digestReadStream)
.map(digestBlobs -> {
final TransientSegment newSegment = transientVersion.newSegment();
newSegment.setWriteSha512(digestReadStream.getDigest(sha512Digest).get())
.setSegmentCipher(null)
.setWriteLength(clearByteCount.count())
.setReadSha512(digestReadStream.getDigest(sha512Digest).get())
.setReadMd5(digestReadStream.getDigest(md5Digest).get())
.setReadLength(clearByteCount.count())
.setIsTinyData(false);
for (DigestBlob digestBlob : digestBlobs) {
newSegment.newBlob()
.setVolumeId(digestBlob.getVolume())
.setPosition(digestBlob.getPosition())
.setReadLength(digestBlob.getLength())
.setReadSha512(digestBlob.getDigest(sha512Digest).get());
}
return newSegment;
});
} else {
BufferWriteEndableWriteStream bufferWriteStream = new BufferWriteEndableWriteStream();
return pump(digestReadStream, bufferWriteStream)
.map(aVoid -> {
final TransientSegment newSegment = transientVersion.newSegment();
newSegment.setWriteSha512(digestReadStream.getDigest(sha512Digest).get())
.setSegmentCipher(null)
.setWriteLength(clearByteCount.count())
.setReadSha512(digestReadStream.getDigest(sha512Digest).get())
.setReadMd5(digestReadStream.getDigest(md5Digest).get())
.setReadLength(clearByteCount.count())
.setIsTinyData(true)
.setTinyData(bufferWriteStream.toBuffer().getBytes());
return newSegment;
});
}
}
}
}