Search in sources :

Example 1 with MessageDigest

use of org.exist.util.crypto.digest.MessageDigest in project exist by eXist-db.

the class BlobStoreImpl method getDigest.

@Override
@Nullable
public MessageDigest getDigest(final Txn transaction, final BlobId blobId, final DigestType digestType) throws IOException {
    if (this.digestType.equals(digestType)) {
        // optimisation, we can just return the BlobId as that is the digest for this digest type!
        return new MessageDigest(digestType, blobId.getId());
    } else {
        // calculate the digest
        final StreamableDigest streamableDigest = digestType.newStreamableDigest();
        final Try<MessageDigest, IOException> result = with(transaction, blobId, maybeBlobFile -> maybeBlobFile == null ? null : TaggedTryUnchecked(IOException.class, () -> {
            FileUtils.digest(maybeBlobFile, streamableDigest);
            return new MessageDigest(streamableDigest.getDigestType(), streamableDigest.getMessageDigest());
        }));
        return result.get();
    }
}
Also used : StreamableDigest(org.exist.util.crypto.digest.StreamableDigest) IOException(java.io.IOException) MessageDigest(org.exist.util.crypto.digest.MessageDigest) Nullable(javax.annotation.Nullable)

Example 2 with MessageDigest

use of org.exist.util.crypto.digest.MessageDigest in project exist by eXist-db.

the class BlobStoreImpl method add.

@Override
public Tuple2<BlobId, Long> add(final Txn transaction, final InputStream is) throws IOException {
    if (state.get() != State.OPEN) {
        throw new IOException("Blob Store is not open!");
    }
    // stage the BLOB file
    final Tuple3<Path, Long, MessageDigest> staged = stage(is);
    final BlobVacuum.RequestDeleteStagedBlobFile requestDeleteStagedBlobFile = new BlobVacuum.RequestDeleteStagedBlobFile(stagingDir, staged._1.getFileName().toString());
    // register a callback to cleanup the staged BLOB file ONLY after commit+checkpoint
    final JournalManager journalManager = database.getJournalManager().orElse(null);
    if (journalManager != null) {
        final DeleteStagedBlobFile cleanupStagedBlob = new DeleteStagedBlobFile(vacuumQueue, requestDeleteStagedBlobFile);
        journalManager.listen(cleanupStagedBlob);
        transaction.registerListener(cleanupStagedBlob);
    }
    final BlobId blobId = new BlobId(staged._3.getValue());
    // if the blob entry does not exist, we exclusively compute it as STAGED.
    BlobReference blobReference = references.computeIfAbsent(blobId, k -> new BlobReference(STAGED));
    try {
        while (true) {
            if (blobReference.count.compareAndSet(STAGED, PROMOTING)) {
                // write journal entries to the WAL
                if (journalManager != null) {
                    try {
                        journalManager.journal(new StoreBlobFileLoggable(transaction.getId(), blobId, staged._1.getFileName().toString()));
                        journalManager.journal(new UpdateBlobRefCountLoggable(transaction.getId(), blobId, 0, 1));
                        // force WAL entries to disk!
                        journalManager.flush(true, true);
                    } catch (final JournalException e) {
                        references.remove(blobId);
                        throw new IOException(e);
                    }
                }
                // promote the staged blob
                promote(staged);
                if (journalManager == null) {
                    // no journal (or recovery)... so go ahead and schedule cleanup of the staged blob file
                    enqueueVacuum(vacuumQueue, requestDeleteStagedBlobFile);
                }
                // schedule disk persist of the new value
                persistQueue.put(Tuple(blobId, blobReference, 1));
                // update memory with the new value
                blobReference.count.set(1);
                // done!
                return Tuple(blobId, staged._2);
            }
            final int count = blobReference.count.get();
            // guard against a concurrent #add or #remove
            if (count == PROMOTING || count == UPDATING_COUNT) {
                // spin whilst another thread promotes the blob, or updates the reference count
                // sleep a small time to save CPU
                Thread.sleep(10);
                continue;
            }
            // i.e. wait for the deletion of the blob to complete, and then we can add the blob again
            if (count == DELETING) {
                blobReference = references.computeIfAbsent(blobId, k -> new BlobReference(STAGED));
                // loop again
                continue;
            }
            // only increment the blob reference if the blob is active!
            if (count >= 0 && blobReference.count.compareAndSet(count, UPDATING_COUNT)) {
                // NOTE: we are the only thread that can be in this branch for the blobId
                final int newCount = count + 1;
                // write journal entries to the WAL
                if (journalManager != null) {
                    try {
                        journalManager.journal(new UpdateBlobRefCountLoggable(transaction.getId(), blobId, count, newCount));
                        // force WAL entries to disk!
                        journalManager.flush(true, true);
                    } catch (final JournalException e) {
                        // restore the state of the blobReference first!
                        blobReference.count.set(count);
                        throw new IOException(e);
                    }
                }
                // persist the new value
                persistQueue.put(Tuple(blobId, blobReference, newCount));
                // update memory with the new value, and release other spinning threads
                blobReference.count.set(newCount);
                // done!
                return Tuple(blobId, staged._2);
            }
        }
    } catch (final InterruptedException e) {
        // thrown by persistQueue.put or Thread.sleep
        Thread.currentThread().interrupt();
        throw new IOException(e);
    }
}
Also used : Path(java.nio.file.Path) Tuple3(com.evolvedbinary.j8fu.tuple.Tuple3) Tuple2(com.evolvedbinary.j8fu.tuple.Tuple2) Txn(org.exist.storage.txn.Txn) JournalException(org.exist.storage.journal.JournalException) RawDataBackup(org.exist.backup.RawDataBackup) ByteBuffer(java.nio.ByteBuffer) FileUtils(org.exist.util.FileUtils) Tuple(com.evolvedbinary.j8fu.tuple.Tuple.Tuple) ThreadUtils.nameInstanceThread(org.exist.util.ThreadUtils.nameInstanceThread) DigestInputStream(org.exist.util.crypto.digest.DigestInputStream) StreamableDigest(org.exist.util.crypto.digest.StreamableDigest) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) DigestType(org.exist.util.crypto.digest.DigestType) LOG_UPDATE_BLOB_REF_COUNT(org.exist.storage.blob.BlobLoggable.LOG_UPDATE_BLOB_REF_COUNT) Path(java.nio.file.Path) java.util.concurrent(java.util.concurrent) StandardOpenOption(java.nio.file.StandardOpenOption) FileNotFoundException(java.io.FileNotFoundException) SeekableByteChannel(java.nio.channels.SeekableByteChannel) Logger(org.apache.logging.log4j.Logger) TxnListener(org.exist.storage.txn.TxnListener) LogEntryTypes(org.exist.storage.journal.LogEntryTypes) java.util(java.util) Try(com.evolvedbinary.j8fu.Try) ThreadSafe(net.jcip.annotations.ThreadSafe) AtomicBoolean(java.util.concurrent.atomic.AtomicBoolean) AtomicReference(java.util.concurrent.atomic.AtomicReference) Function(java.util.function.Function) FilterInputStream(java.io.FilterInputStream) LOG_STORE_BLOB_FILE(org.exist.storage.blob.BlobLoggable.LOG_STORE_BLOB_FILE) ThreadUtils.newInstanceSubThreadGroup(org.exist.util.ThreadUtils.newInstanceSubThreadGroup) LogException(org.exist.storage.journal.LogException) REPLACE_EXISTING(java.nio.file.StandardCopyOption.REPLACE_EXISTING) Nullable(javax.annotation.Nullable) OutputStream(java.io.OutputStream) Database(org.exist.Database) ATOMIC_MOVE(java.nio.file.StandardCopyOption.ATOMIC_MOVE) CountingInputStream(org.apache.commons.io.input.CountingInputStream) Files(java.nio.file.Files) HexEncoder.bytesToHex(org.exist.util.HexEncoder.bytesToHex) JournalManager(org.exist.storage.journal.JournalManager) IOException(java.io.IOException) BlobReference(org.exist.storage.blob.BlobStoreImpl.BlobReference) UUIDGenerator(org.exist.util.UUIDGenerator) MessageDigest(org.exist.util.crypto.digest.MessageDigest) FileUtils.fileName(org.exist.util.FileUtils.fileName) TaggedTryUnchecked(com.evolvedbinary.j8fu.Try.TaggedTryUnchecked) LogManager(org.apache.logging.log4j.LogManager) InputStream(java.io.InputStream) BlobReference(org.exist.storage.blob.BlobStoreImpl.BlobReference) JournalException(org.exist.storage.journal.JournalException) JournalManager(org.exist.storage.journal.JournalManager) IOException(java.io.IOException) MessageDigest(org.exist.util.crypto.digest.MessageDigest)

Example 3 with MessageDigest

use of org.exist.util.crypto.digest.MessageDigest in project exist by eXist-db.

the class BinaryDoc method eval.

@Override
public Sequence eval(final Sequence[] args, final Sequence contextSequence) throws XPathException {
    final Sequence emptyParamReturnValue = (isCalledAs(FS_BINARY_DOC_NAME) || isCalledAs(FS_BINARY_DOC_CONTENT_DIGEST_NAME)) ? Sequence.EMPTY_SEQUENCE : BooleanValue.FALSE;
    if (args[0].isEmpty()) {
        return emptyParamReturnValue;
    }
    final String path = args[0].getStringValue();
    try (final LockedDocument lockedDoc = context.getBroker().getXMLResource(XmldbURI.xmldbUriFor(path), LockMode.READ_LOCK)) {
        if (lockedDoc == null) {
            return emptyParamReturnValue;
        }
        final DocumentImpl doc = lockedDoc.getDocument();
        if (doc.getResourceType() != DocumentImpl.BINARY_FILE) {
            return emptyParamReturnValue;
        } else if (isCalledAs(FS_BINARY_DOC_NAME)) {
            try (final Txn transaction = context.getBroker().continueOrBeginTransaction()) {
                final BinaryDocument bin = (BinaryDocument) doc;
                final InputStream is = context.getBroker().getBinaryResource(transaction, bin);
                final Base64BinaryDocument b64doc = Base64BinaryDocument.getInstance(context, is);
                b64doc.setUrl(path);
                transaction.commit();
                return b64doc;
            }
        } else if (isCalledAs(FS_BINARY_DOC_CONTENT_DIGEST_NAME)) {
            final String algorithm = args[1].getStringValue();
            final DigestType digestType;
            try {
                digestType = DigestType.forCommonName(algorithm);
            } catch (final IllegalArgumentException e) {
                throw new XPathException(this, "Invalid algorithm: " + algorithm, e);
            }
            try (final Txn transaction = context.getBroker().getBrokerPool().getTransactionManager().beginTransaction()) {
                final BinaryDocument bin = (BinaryDocument) doc;
                final MessageDigest messageDigest = context.getBroker().getBinaryResourceContentDigest(transaction, bin, digestType);
                final InputStream is = new UnsynchronizedByteArrayInputStream(messageDigest.getValue());
                final Sequence result = BinaryValueFromInputStream.getInstance(context, new HexBinaryValueType(), is);
                transaction.commit();
                return result;
            }
        } else {
            return BooleanValue.TRUE;
        }
    } catch (final URISyntaxException e) {
        logger.error("Invalid resource URI", e);
        throw new XPathException(this, "Invalid resource uri", e);
    } catch (final PermissionDeniedException e) {
        logger.error("{}: permission denied to read resource", path, e);
        throw new XPathException(this, path + ": permission denied to read resource");
    } catch (final IOException | TransactionException e) {
        logger.error("{}: I/O error while reading resource", path, e);
        throw new XPathException(this, path + ": I/O error while reading resource", e);
    }
}
Also used : XPathException(org.exist.xquery.XPathException) UnsynchronizedByteArrayInputStream(org.apache.commons.io.input.UnsynchronizedByteArrayInputStream) InputStream(java.io.InputStream) Txn(org.exist.storage.txn.Txn) URISyntaxException(java.net.URISyntaxException) IOException(java.io.IOException) DocumentImpl(org.exist.dom.persistent.DocumentImpl) BinaryDocument(org.exist.dom.persistent.BinaryDocument) TransactionException(org.exist.storage.txn.TransactionException) DigestType(org.exist.util.crypto.digest.DigestType) LockedDocument(org.exist.dom.persistent.LockedDocument) UnsynchronizedByteArrayInputStream(org.apache.commons.io.input.UnsynchronizedByteArrayInputStream) PermissionDeniedException(org.exist.security.PermissionDeniedException) MessageDigest(org.exist.util.crypto.digest.MessageDigest)

Example 4 with MessageDigest

use of org.exist.util.crypto.digest.MessageDigest in project exist by eXist-db.

the class RemoteBinaryResource method getContentDigest.

@Override
public MessageDigest getContentDigest(final DigestType digestType) throws XMLDBException {
    if (contentDigest != null && contentDigest.getDigestType().equals(digestType)) {
        return contentDigest;
    }
    final List<String> params = new ArrayList<>();
    params.add(path.toString());
    params.add(digestType.getCommonNames()[0]);
    final Map result = (Map) collection.execute("getContentDigest", params);
    final String digestAlgorithm = (String) result.get("digest-algorithm");
    final byte[] digest = (byte[]) result.get("digest");
    final MessageDigest messageDigest = new MessageDigest(DigestType.forCommonName(digestAlgorithm), digest);
    if (this.contentDigest == null) {
        this.contentDigest = messageDigest;
    }
    return messageDigest;
}
Also used : ArrayList(java.util.ArrayList) MessageDigest(org.exist.util.crypto.digest.MessageDigest) Map(java.util.Map)

Example 5 with MessageDigest

use of org.exist.util.crypto.digest.MessageDigest in project exist by eXist-db.

the class RemoteCollection method getResource.

@Override
public Resource getResource(final String name) throws XMLDBException {
    final List<String> params = new ArrayList<>(1);
    XmldbURI docUri;
    try {
        docUri = XmldbURI.xmldbUriFor(name);
    } catch (final URISyntaxException e) {
        throw new XMLDBException(ErrorCodes.INVALID_URI, e);
    }
    params.add(getPathURI().append(docUri).toString());
    final Map hash;
    hash = (Map) execute("describeResource", params);
    final String docName = (String) hash.get("name");
    if (docName == null) {
        // resource does not exist!
        return null;
    }
    try {
        docUri = XmldbURI.xmldbUriFor(docName).lastSegment();
    } catch (final URISyntaxException e) {
        throw new XMLDBException(ErrorCodes.INVALID_URI, e);
    }
    final String owner = (String) hash.get("owner");
    final String group = (String) hash.get("group");
    final int mode = (Integer) hash.get("permissions");
    final Stream<ACEAider> aces = extractAces(hash.get("acl"));
    final Permission perm;
    try {
        perm = getPermission(owner, group, mode, aces);
    } catch (final PermissionDeniedException pde) {
        throw new XMLDBException(ErrorCodes.PERMISSION_DENIED, "Unable to retrieve permissions for resource '" + name + "': " + pde.getMessage(), pde);
    }
    final String type = (String) hash.get("type");
    long contentLen = 0;
    if (hash.containsKey("content-length-64bit")) {
        final Object o = hash.get("content-length-64bit");
        if (o instanceof Long) {
            contentLen = (Long) o;
        } else {
            contentLen = Long.parseLong((String) o);
        }
    } else if (hash.containsKey("content-length")) {
        contentLen = (Integer) hash.get("content-length");
    }
    final AbstractRemoteResource r;
    if (type == null || "XMLResource".equals(type)) {
        r = new RemoteXMLResource(this, -1, -1, docUri, Optional.empty());
    } else {
        r = new RemoteBinaryResource(this, docUri);
        if (hash.containsKey("blob-id")) {
            final byte[] blobId = (byte[]) hash.get("blob-id");
            ((RemoteBinaryResource) r).setBlobId(new BlobId(blobId));
        }
        if (hash.containsKey("digest-algorithm") && hash.containsKey("digest")) {
            final String digestAlgorithm = (String) hash.get("digest-algorithm");
            final byte[] digest = (byte[]) hash.get("digest");
            final MessageDigest messageDigest = new MessageDigest(DigestType.forCommonName(digestAlgorithm), digest);
            ((RemoteBinaryResource) r).setContentDigest(messageDigest);
        }
    }
    r.setPermissions(perm);
    r.setContentLength(contentLen);
    r.dateCreated = (Date) hash.get("created");
    r.dateModified = (Date) hash.get("modified");
    if (hash.containsKey("mime-type")) {
        r.setMimeType((String) hash.get("mime-type"));
    }
    return r;
}
Also used : ArrayList(java.util.ArrayList) URISyntaxException(java.net.URISyntaxException) ACEAider(org.exist.security.internal.aider.ACEAider) Permission(org.exist.security.Permission) PermissionDeniedException(org.exist.security.PermissionDeniedException) MessageDigest(org.exist.util.crypto.digest.MessageDigest) Map(java.util.Map) BlobId(org.exist.storage.blob.BlobId)

Aggregations

MessageDigest (org.exist.util.crypto.digest.MessageDigest)30 Path (java.nio.file.Path)20 Test (org.junit.Test)19 InputStream (java.io.InputStream)6 UnsynchronizedByteArrayInputStream (org.apache.commons.io.input.UnsynchronizedByteArrayInputStream)5 DigestInputStream (org.exist.util.crypto.digest.DigestInputStream)5 StreamableDigest (org.exist.util.crypto.digest.StreamableDigest)4 Tuple2 (com.evolvedbinary.j8fu.tuple.Tuple2)3 IOException (java.io.IOException)3 URISyntaxException (java.net.URISyntaxException)3 DigestType (org.exist.util.crypto.digest.DigestType)3 ArrayList (java.util.ArrayList)2 Map (java.util.Map)2 Nullable (javax.annotation.Nullable)2 EXistException (org.exist.EXistException)2 PermissionDeniedException (org.exist.security.PermissionDeniedException)2 Txn (org.exist.storage.txn.Txn)2 Try (com.evolvedbinary.j8fu.Try)1 TaggedTryUnchecked (com.evolvedbinary.j8fu.Try.TaggedTryUnchecked)1 Tuple (com.evolvedbinary.j8fu.tuple.Tuple.Tuple)1