/* * Created on 30-Nov-2005 * Created by Paul Gardner * Copyright (C) 2005, 2006 Aelitis, All Rights Reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * * AELITIS, SAS au capital de 40,000 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package com.aelitis.azureus.core.diskmanager.file.impl; import java.io.File; import java.io.RandomAccessFile; import org.gudy.azureus2.core3.torrent.TOTorrent; import org.gudy.azureus2.core3.torrent.TOTorrentFile; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.DirectByteBuffer; import org.gudy.azureus2.core3.util.DirectByteBufferPool; import com.aelitis.azureus.core.diskmanager.file.FMFile; import com.aelitis.azureus.core.diskmanager.file.FMFileManagerException; public class FMFileAccessController implements FMFileAccess { private FMFileImpl owner; private int type = FMFile.FT_LINEAR; private File control_file; private FMFileAccess file_access; protected FMFileAccessController( FMFileImpl _file, int _target_type ) throws FMFileManagerException { owner = _file; // actual file shouldn't exist for change to occur - it is the responsibility // of the caller to delete the file first and take consequent actions (in // particular force recheck the file to ensure that the loss in save state // is represented in the resume view of the world ) // in the future, if we support format conversion, this obviously changes control_file = getControlFile(); if ( control_file == null ){ // Debug.out( "No control file" ); in optimised environments we don't support compact and return null here if ( _target_type == FMFile.FT_LINEAR ){ file_access = new FMFileAccessLinear( owner ); }else{ throw( new FMFileManagerException( "Compact storage not supported: no control file available" )); } }else{ boolean control_file_existed = control_file.exists(); type = control_file_existed?FMFile.FT_COMPACT:FMFile.FT_LINEAR; if ( type == FMFile.FT_LINEAR ){ file_access = new FMFileAccessLinear( owner ); }else{ file_access = new FMFileAccessCompact( owner.getOwner().getTorrentFile(), control_file, new FMFileAccessLinear( owner )); } if ( type != _target_type ){ convert( _target_type ); } } } protected void convert( int target_type ) throws FMFileManagerException { File file = owner.getLinkedFile(); RandomAccessFile raf = null; boolean ok = false; try{ FMFileAccess target_access; if ( target_type == FMFile.FT_LINEAR ){ target_access = new FMFileAccessLinear( owner ); }else{ target_access = new FMFileAccessCompact( owner.getOwner().getTorrentFile(), control_file, new FMFileAccessLinear( owner )); } if ( file.exists()){ raf = new RandomAccessFile( file, FMFileImpl.WRITE_ACCESS_MODE); // due to the simplistic implementation of compact we only actually need to deal with // the last piece of the file (first piece is in the right place already) FMFileAccessCompact compact_access; if ( target_type == FMFile.FT_LINEAR ){ compact_access = (FMFileAccessCompact)file_access; }else{ compact_access = (FMFileAccessCompact)target_access; } long length = file_access.getLength( raf ); long last_piece_start = compact_access.getLastPieceStart(); long last_piece_length = compact_access.getLastPieceLength(); // see if we have any potential data for the last piece if ( last_piece_length > 0 && length > last_piece_start ){ long data_length = length - last_piece_start; if ( data_length > last_piece_length ){ Debug.out("data length inconsistent: len=" + data_length + ",limit=" + last_piece_length ); data_length = last_piece_length; } DirectByteBuffer buffer = DirectByteBufferPool.getBuffer( DirectByteBuffer.AL_FILE, (int)data_length ); try{ file_access.read( raf, new DirectByteBuffer[]{ buffer }, last_piece_start ); // see if we need to truncate if ( target_type == FMFile.FT_COMPACT ){ long first_piece_length = compact_access.getFirstPieceLength(); long physical_length = raf.length(); if ( physical_length > first_piece_length ){ raf.setLength( first_piece_length ); } } buffer.flip( DirectByteBuffer.AL_FILE ); target_access.write( raf, new DirectByteBuffer[]{ buffer }, last_piece_start ); }finally{ buffer.returnToPool(); } }else{ // no last piece, truncate after the first piece if ( target_type == FMFile.FT_COMPACT ){ long first_piece_length = compact_access.getFirstPieceLength(); long physical_length = raf.length(); if ( physical_length > first_piece_length ){ raf.setLength( first_piece_length ); } } } target_access.setLength( raf, length ); target_access.flush(); } type = target_type; file_access = target_access; ok = true; }catch( Throwable e ){ Debug.printStackTrace( e ); throw( new FMFileManagerException( "convert fails", e )); }finally{ try{ if ( raf != null ){ try{ raf.close(); }catch( Throwable e ){ // override original exception if there isn't one if ( ok ){ ok = false; throw( new FMFileManagerException( "convert fails", e )); } } } }finally{ if ( !ok ){ // conversion failed - replace with linear access, caller is responsible for // handling this (marking file requiring recheck) type = FMFile.FT_LINEAR; file_access = new FMFileAccessLinear( owner ); } if ( type == FMFile.FT_LINEAR ){ control_file.delete(); } } } } protected File getControlFile() { TOTorrentFile tf = owner.getOwner().getTorrentFile(); if ( tf == null ){ return( null ); } TOTorrent torrent = tf.getTorrent(); TOTorrentFile[] files = torrent.getFiles(); int file_index = -1; for (int i=0;i<files.length;i++){ if ( files[i] == tf ){ file_index = i; break; } } if ( file_index == -1 ){ Debug.out("File '" + owner.getName() + "' not found in torrent!" ); return( null ); }else{ File control = owner.getOwner().getControlFile( "fmfile" + file_index + ".dat" ); return( control ); } } public void setStorageType( int new_type ) throws FMFileManagerException { if ( new_type == type ){ return; } convert( new_type ); } public int getStorageType() { return( type ); } // FileAccess public long getLength( RandomAccessFile raf ) throws FMFileManagerException { return( file_access.getLength( raf )); } public void setLength( RandomAccessFile raf, long length ) throws FMFileManagerException { file_access.setLength( raf, length ); } public void read( RandomAccessFile raf, DirectByteBuffer[] buffers, long offset ) throws FMFileManagerException { file_access.read( raf, buffers, offset ); } public void write( RandomAccessFile raf, DirectByteBuffer[] buffers, long position ) throws FMFileManagerException { file_access.write( raf, buffers, position ); } public void flush() throws FMFileManagerException { file_access.flush(); } public String getString() { return( "type=" + type + ",acc=" + file_access.getString()); } }