public ByteProvider getByteProvider(FSRL fsrl, boolean fullyQualifiedFSRL, TaskMonitor monitor)
			throws CancelledException, IOException {

		if ( fsrl.getMD5() != null ) {
			FileCacheEntry fce = fileCache.getFileCacheEntry(fsrl.getMD5());
			if ( fce != null ) {
				return fce.asByteProvider(fsrl);
			}
		}

		try ( FileSystemRef fsRef = getFilesystem(fsrl.getFS(), monitor) ) {
			GFileSystem fs = fsRef.getFilesystem();
			GFile file = fs.lookup(fsrl.getPath());
			if (file == null) {
				throw new IOException("File not found: " + fsrl);
			}
			if (file.getFSRL().getMD5() != null) {
				fsrl = file.getFSRL();
				// try again to fetch cached file if we now have a md5
				FileCacheEntry fce = fileCache.getFileCacheEntry(fsrl.getMD5());
				if (fce != null) {
					return fce.asByteProvider(fsrl);
				}
			}
			ByteProvider provider = fs.getByteProvider(file, monitor);
			if (provider == null) {
				throw new IOException("Unable to get ByteProvider for " + fsrl);
			}

			// use the returned provider's FSRL as it may have more info
			FSRL resultFSRL = provider.getFSRL();
			if (resultFSRL.getMD5() == null && (fsrl.getMD5() != null || fullyQualifiedFSRL)) {
				String md5 = (fs instanceof GFileHashProvider)
						? ((GFileHashProvider) fs).getMD5Hash(file, true, monitor)
						: FSUtilities.getMD5(provider, monitor);
				resultFSRL = resultFSRL.withMD5(md5);
			}
			if (fsrl.getMD5() != null) {
				if (!fsrl.isMD5Equal(resultFSRL.getMD5())) {
					throw new IOException("Unable to retrieve requested file, hash has changed: " +
						fsrl + ", new hash: " + resultFSRL.getMD5());
				}
			}
			return new RefdByteProvider(fsRef.dup(), provider, resultFSRL);
		}
	}
Returns a {@link ByteProvider} with the contents of the requested {@link GFile file}.
<p>
Never returns null, throws IOException if there was a problem.
<p>
Caller is responsible for {@link ByteProvider#close() closing()} the ByteProvider
when finished.

@param fsrl {@link FSRL} file to wrap
@param fullyQualifiedFSRL if true, the returned ByteProvider's FSRL will always have a MD5
hash
@param monitor {@link TaskMonitor} to watch and update
@return new {@link ByteProvider}
@throws CancelledException if user cancels
@throws IOException if IO problem
Ghidra/Features/Base/src/main/java/ghidra/formats/gfilesystem/FileSystemService.java