/** * Copies a source file to a destination file, optionally preserving the source's last * modification time. We already have an input stream to read the source file and the destination * appears to be an entry in an archive file. Note that this method <em>never</em> closes the * given input stream! * * <p>Note that this method synchronizes on the class object in order to prevent dead locks by two * threads copying archive entries to the other's source archive concurrently! * * @throws FalsePositiveException If the destination is a false positive and the exception cannot * get resolved within this method. * @throws InputIOException If copying the data fails because of an IOException in the source. * @throws IOException If copying the data fails because of an IOException in the destination. */ static final void cp0( final boolean preserve, final java.io.File src, final InputStream in, final ArchiveController dstController, final String dstEntryName) throws IOException { // Do not assume anything about the lock status of the controller: // This method may be called from a subclass while a lock is acquired! // assert !dstController.readLock().isLocked(); // assert !dstController.writeLock().isLocked(); try { class OStreamCreator implements IORunnable { OutputStream out; // = null; public void run() throws IOException { // Update controller. // This may invalidate the file system object, so it must be // done first in case srcController and dstController are the // same! dstController.autoUmount(dstEntryName); final boolean lenient = File.isLenient(); // Get source archive entry. final ArchiveEntry srcEntry = new RfsEntry(src); // Get destination archive entry. final ArchiveFileSystem dstFileSystem = dstController.autoMount(lenient); final Delta delta = dstFileSystem.link(dstEntryName, lenient, preserve ? srcEntry : null); final ArchiveEntry dstEntry = delta.getEntry(); // Create output stream. out = dstController.createOutputStream(dstEntry, srcEntry); // Now link the destination entry into the file system. delta.commit(); } } // Create the output stream while the destination controller is // write locked. final OStreamCreator stream = new OStreamCreator(); dstController.runWriteLocked(stream); final OutputStream out = stream.out; // Finally copy the entry data. try { Streams.cat(in, out); } finally { out.close(); } } catch (ArchiveEntryFalsePositiveException ex) { assert dstController == ex.getController(); // Reroute call to the destination's enclosing ArchiveController. cp0( preserve, src, in, dstController.getEnclController(), dstController.enclEntryName(dstEntryName)); } }
/** * Copies a source file to a destination file, optionally preserving the source's last * modification time. We know that the source and destination files both appear to be entries in * an archive file. * * @throws FalsePositiveException If the source or the destination is a false positive and the * exception for the destination cannot get resolved within this method. * @throws InputIOException If copying the data fails because of an IOException in the source. * @throws IOException If copying the data fails because of an IOException in the destination. */ private static void cp0( final boolean preserve, final ArchiveController srcController, final String srcEntryName, final ArchiveController dstController, final String dstEntryName) throws IOException { // Do not assume anything about the lock status of the controller: // This method may be called from a subclass while a lock is acquired! // assert !srcController.readLock().isLocked(); // assert !srcController.writeLock().isLocked(); // assert !dstController.readLock().isLocked(); // assert !dstController.writeLock().isLocked(); try { class IOStreamCreator implements IORunnable { InputStream in; OutputStream out; public void run() throws IOException { // Update controllers. // This may invalidate the file system object, so it must be // done first in case srcController and dstController are the // same! class SrcControllerUpdater implements IORunnable { public void run() throws IOException { srcController.autoUmount(srcEntryName); srcController.readLock().lock(); // downgrade to read lock upon return } } // class SrcControllerUpdater final ArchiveEntry srcEntry, dstEntry; final Delta delta; srcController.runWriteLocked(new SrcControllerUpdater()); try { dstController.autoUmount(dstEntryName); // Get source archive entry. final ArchiveFileSystem srcFileSystem = srcController.autoMount(false); srcEntry = srcFileSystem.get(srcEntryName); // Get destination archive entry. final boolean lenient = File.isLenient(); final ArchiveFileSystem dstFileSystem = dstController.autoMount(lenient); delta = dstFileSystem.link(dstEntryName, lenient, preserve ? srcEntry : null); dstEntry = delta.getEntry(); // Create input stream. in = srcController.createInputStream(srcEntry, dstEntry); } finally { srcController.readLock().unlock(); } try { // Create output stream. out = dstController.createOutputStream(dstEntry, srcEntry); try { // Now link the destination entry into the file system. delta.commit(); } catch (IOException ex) { out.close(); throw ex; } } catch (IOException ex) { try { in.close(); } catch (IOException inFailure) { throw new InputIOException(inFailure); } throw ex; } } } // class IOStreamCreator final IOStreamCreator streams = new IOStreamCreator(); synchronized (copyLock) { dstController.runWriteLocked(streams); } // Finally copy the entry data. cp(streams.in, streams.out); } catch (ArchiveEntryFalsePositiveException ex) { // Both the source and/or the destination may be false positives, // so we need to use the exception's additional information to // find out which controller actually detected the false positive. if (dstController != ex.getController()) throw ex; // not my job - pass on! // Reroute call to the destination's enclosing archive controller. cp0( preserve, srcController, srcEntryName, dstController.getEnclController(), dstController.enclEntryName(dstEntryName)); } }