As mentioned in https://github.com/elastic/elasticsearch/pull/96216, `LuceneVerifyingIndexOutput` always copies the last chunk byte-by-byte. This might be a pretty big chunk. We should be able to copy all the bytes up to the start of the checksum in bulk (and then I guess bulk-copy the checksum too not that this matters so much).

In the same area, `LuceneVerifyingIndexOutput` is the only subclass of `VerifyingIndexOutput` and there's no need for it to be an inner class of `Store`. `VerifyingIndexInput` also doesn't need to be an inner class.
server/src/main/java/org/elasticsearch/index/store/Store.java
fileHash.grow(len);
            fileHash.setLength(len);
            final int readBytes = Streams.readFully(in, fileHash.bytes(), 0, len);
            assert readBytes == len : Integer.toString(readBytes) + " != " + Integer.toString(len);
            assert fileHash.length() == len : Integer.toString(fileHash.length()) + " != " + Integer.toString(len);
        }
        @Override
        public Iterator<StoreFileMetadata> iterator() {
            return fileMetadataMap.values().iterator();
        }
        public StoreFileMetadata get(String name) {
            return fileMetadataMap.get(name);
        }
        private static final String SEGMENT_INFO_EXTENSION = "si";
        /**
         * Returns a diff between the two snapshots that can be used for recovery. The given snapshot is treated as the
         * recovery target and this snapshot as the source. The returned diff will hold a list of files that are:
         * <ul>
         * <li>identical: they exist in both snapshots and they can be considered the same ie. they don't need to be recovered</li>
         * <li>different: they exist in both snapshots but their they are not identical</li>
         * <li>missing: files that exist in the source but not in the target</li>
         * </ul>
         * <p>
         * Individual files are compared by name, length, checksum and (if present) a UUID that was assigned when the file was originally
         * written. The segment info ({@code *.si}) files and the segments file ({@code segments_N}) are also checked to be a byte-for-byte
         * match.
         * <p>
         * Files are collected together into a group for each segment plus one group of "per-commit" ({@code segments_N}) files. Each
         * per-segment group is subdivided into a nongenerational group (most of them) and a generational group (e.g. {@code *.liv},
         * {@code *.fnm}, {@code *.dvm}, {@code *.dvd} that have been updated by subsequent commits).