Changeset f9b2c0bc


Ignore:
Timestamp:
Mar 8, 2011 3:01:02 AM (9 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
b02fe53
Parents:
0e854623
Message:
  • i2psnark:
    • More efficient metainfo handling, reduce instantiations
    • Improved handling of storage errors
    • Improved handling of duplicate file names
    • More metainfo sanity checks
    • Metadata transfer error handling improvements
    • Code cleanup, remove dead and duplicated code
Location:
apps/i2psnark/java/src/org/klomp/snark
Files:
8 edited

Legend:

Unmodified
Added
Removed
  • apps/i2psnark/java/src/org/klomp/snark/MagnetState.java

    r0e854623 rf9b2c0bc  
    192192    public MetaInfo buildMetaInfo() throws Exception {
    193193        // top map has nothing in it but the info map (no announce)
    194         Map<String, Object> map = new HashMap();
     194        Map<String, BEValue> map = new HashMap();
    195195        InputStream is = new ByteArrayInputStream(metainfoBytes);
    196196        BDecoder dec = new BDecoder(is);
     
    198198        map.put("info", bev);
    199199        MetaInfo newmeta = new MetaInfo(map);
    200         if (!DataHelper.eq(newmeta.getInfoHash(), infohash))
     200        if (!DataHelper.eq(newmeta.getInfoHash(), infohash)) {
     201            // Disaster. Start over. ExtensionHandler will catch
     202            // the IOE and disconnect the peer, hopefully we will
     203            // find a new peer.
     204            // TODO: Count fails and give up eventually
     205            have = new BitField(totalChunks);
     206            requested = new BitField(totalChunks);
    201207            throw new IOException("info hash mismatch");
     208        }
    202209        return newmeta;
    203210    }
  • apps/i2psnark/java/src/org/klomp/snark/MetaInfo.java

    r0e854623 rf9b2c0bc  
    2121package org.klomp.snark;
    2222
     23import java.io.FileInputStream;
    2324import java.io.IOException;
    2425import java.io.InputStream;
     
    3536import net.i2p.crypto.SHA1;
    3637import net.i2p.data.Base64;
     38import net.i2p.data.DataHelper;
    3739import net.i2p.util.Log;
    3840
     
    8385    this.length = length;
    8486
     87    // TODO if we add a parameter for other keys
     88    //if (other != null) {
     89    //    otherInfo = new HashMap(2);
     90    //    otherInfo.putAll(other);
     91    //}
     92
    8593    this.info_hash = calculateInfoHash();
    8694    //infoMap = null;
     
    102110   * must have a complete dictionary describing the torrent.
    103111   */
    104   public MetaInfo(BDecoder be) throws IOException
     112  private MetaInfo(BDecoder be) throws IOException
    105113  {
    106114    // Note that evaluation order matters here...
    107115    this(be.bdecodeMap().getMap());
     116    byte[] origInfohash = be.get_special_map_digest();
     117    // shouldn't ever happen
     118    if (!DataHelper.eq(origInfohash, info_hash))
     119        throw new InvalidBEncodingException("Infohash mismatch, please report");
    108120  }
    109121
     
    117129   * contain a valid info dictionary.
    118130   */
    119   public MetaInfo(Map m) throws InvalidBEncodingException
     131  public MetaInfo(Map<String, BEValue> m) throws InvalidBEncodingException
    120132  {
    121133    if (_log.shouldLog(Log.DEBUG))
    122134        _log.debug("Creating a metaInfo: " + m, new Exception("source"));
    123     BEValue val = (BEValue)m.get("announce");
     135    BEValue val = m.get("announce");
    124136    // Disabled check, we can get info from a magnet now
    125137    if (val == null) {
     
    130142    }
    131143
    132     val = (BEValue)m.get("info");
     144    val = m.get("info");
    133145    if (val == null)
    134146        throw new InvalidBEncodingException("Missing info map");
    135     Map info = val.getMap();
     147    Map<String, BEValue> info = val.getMap();
    136148    infoMap = Collections.unmodifiableMap(info);
    137149
    138     val = (BEValue)info.get("name");
     150    val = info.get("name");
    139151    if (val == null)
    140152        throw new InvalidBEncodingException("Missing name string");
    141153    name = val.getString();
    142 
    143     val = (BEValue)info.get("name.utf-8");
     154    // We could silently replace the '/', but that messes up the info hash, so just throw instead.
     155    if (name.indexOf('/') >= 0)
     156        throw new InvalidBEncodingException("Invalid name containing '/' " + name);
     157
     158    val = info.get("name.utf-8");
    144159    if (val != null)
    145160        name_utf8 = val.getString();
     
    147162        name_utf8 = null;
    148163
    149     val = (BEValue)info.get("piece length");
     164    val = info.get("piece length");
    150165    if (val == null)
    151166        throw new InvalidBEncodingException("Missing piece length number");
    152167    piece_length = val.getInt();
    153168
    154     val = (BEValue)info.get("pieces");
     169    val = info.get("pieces");
    155170    if (val == null)
    156171        throw new InvalidBEncodingException("Missing piece bytes");
    157172    piece_hashes = val.getBytes();
    158173
    159     val = (BEValue)info.get("length");
     174    val = info.get("length");
    160175    if (val != null)
    161176      {
     
    169184      {
    170185        // Multi file case.
    171         val = (BEValue)info.get("files");
     186        val = info.get("files");
    172187        if (val == null)
    173188          throw new InvalidBEncodingException
     
    190205              throw new InvalidBEncodingException("Missing length number");
    191206            long len = val.getLong();
     207            if (len < 0)
     208              throw new InvalidBEncodingException("Negative file length");
    192209            m_lengths.add(Long.valueOf(len));
     210            // check for overflowing the long
     211            long oldTotal = l;
    193212            l += len;
     213            if (l < oldTotal)
     214              throw new InvalidBEncodingException("Huge total length");
    194215
    195216            val = (BEValue)desc.get("path");
     
    203224            List<String> file = new ArrayList(path_length);
    204225            Iterator<BEValue> it = path_list.iterator();
    205             while (it.hasNext())
    206               file.add(it.next().getString());
     226            while (it.hasNext()) {
     227                String s = it.next().getString();
     228                // We could throw an IBEE, but just silently replace instead.
     229                if (s.indexOf('/') >= 0)
     230                    s = s.replace("/", "_");
     231                file.add(s);
     232            }
     233
     234            // quick dup check - case sensitive, etc. - Storage does a better job
     235            for (int j = 0; j < i; j++) {
     236                if (file.equals(m_files.get(j)))
     237                    throw new InvalidBEncodingException("Duplicate file path " + DataHelper.toString(file));
     238            }
    207239
    208240            m_files.add(Collections.unmodifiableList(file));
     
    231263
    232264  /**
     265   * Efficiently returns the name and the 20 byte SHA1 hash of the info dictionary in a torrent file
     266   * Caller must close stream.
     267   *
     268   * @param infoHashOut 20-byte out parameter
     269   * @since 0.8.5
     270   */
     271  public static String getNameAndInfoHash(InputStream in, byte[] infoHashOut) throws IOException {
     272      BDecoder bd = new BDecoder(in);
     273      Map<String, BEValue> m = bd.bdecodeMap().getMap();
     274      BEValue ibev = m.get("info");
     275      if (ibev == null)
     276          throw new InvalidBEncodingException("Missing info map");
     277      Map<String, BEValue> i = ibev.getMap();
     278      BEValue rvbev = i.get("name");
     279      if (rvbev == null)
     280          throw new InvalidBEncodingException("Missing name");
     281      byte[] h = bd.get_special_map_digest();
     282      System.arraycopy(h, 0, infoHashOut, 0, 20);
     283      return rvbev.getString();
     284  }
     285
     286  /**
    233287   * Returns the string representing the URL of the tracker for this torrent.
    234288   * @return may be null!
     
    319373  public boolean checkPiece(int piece, byte[] bs, int off, int length)
    320374  {
    321     if (true)
     375    //if (true)
    322376        return fast_checkPiece(piece, bs, off, length);
    323     else
    324         return orig_checkPiece(piece, bs, off, length);
    325   }
     377    //else
     378    //    return orig_checkPiece(piece, bs, off, length);
     379  }
     380
     381/****
    326382  private boolean orig_checkPiece(int piece, byte[] bs, int off, int length) {
    327383    // Check digest
     
    343399    return true;
    344400  }
     401****/
    345402 
    346403  private boolean fast_checkPiece(int piece, byte[] bs, int off, int length) {
     
    366423  public String toString()
    367424  {
    368     return "MetaInfo[info_hash='" + hexencode(info_hash)
     425    return "MetaInfo[info_hash='" + I2PSnarkUtil.toHex(info_hash)
    369426      + "', announce='" + announce
    370427      + "', name='" + name
     
    377434
    378435  /**
    379    * Encode a byte array as a hex encoded string.
    380    */
    381   private static String hexencode(byte[] bs)
    382   {
    383     StringBuilder sb = new StringBuilder(bs.length*2);
    384     for (int i = 0; i < bs.length; i++)
    385       {
    386         int c = bs[i] & 0xFF;
    387         if (c < 16)
    388           sb.append('0');
    389         sb.append(Integer.toHexString(c));
    390       }
    391 
    392     return sb.toString();
    393   }
    394 
    395   /**
    396436   * Creates a copy of this MetaInfo that shares everything except the
    397437   * announce URL.
     
    428468  private Map<String, BEValue> createInfoMap()
    429469  {
    430     // if we loaded this metainfo from a file, we have the map
     470    // If we loaded this metainfo from a file, we have the map, and we must use it
     471    // or else we will lose any non-standard keys and corrupt the infohash.
    431472    if (infoMap != null)
    432473        return Collections.unmodifiableMap(infoMap);
     
    454495        info.put("files", l);
    455496      }
     497
     498    // TODO if we add the ability for other keys in the first constructor
     499    //if (otherInfo != null)
     500    //    info.putAll(otherInfo);
     501
    456502    infoMap = info;
    457503    return Collections.unmodifiableMap(infoMap);
     
    460506  private byte[] calculateInfoHash()
    461507  {
    462     Map info = createInfoMap();
    463     StringBuilder buf = new StringBuilder(128);
    464     buf.append("info: ");
    465     for (Iterator iter = info.entrySet().iterator(); iter.hasNext(); ) {
    466         Map.Entry entry = (Map.Entry)iter.next();
    467         String key = (String)entry.getKey();
    468         Object val = entry.getValue();
    469         buf.append(key).append('=');
    470         if (val instanceof byte[])
    471             buf.append(Base64.encode((byte[])val, true));
    472         else
     508    Map<String, BEValue> info = createInfoMap();
     509    if (_log.shouldLog(Log.DEBUG)) {
     510        StringBuilder buf = new StringBuilder(128);
     511        buf.append("info: ");
     512        for (Map.Entry<String, BEValue> entry : info.entrySet()) {
     513            String key = entry.getKey();
     514            Object val = entry.getValue();
     515            buf.append(key).append('=');
    473516            buf.append(val.toString());
     517        }
     518        _log.debug(buf.toString());
    474519    }
    475     if (_log.shouldLog(Log.DEBUG))
    476         _log.debug(buf.toString());
    477520    byte[] infoBytes = BEncoder.bencode(info);
    478521    //_log.debug("info bencoded: [" + Base64.encode(infoBytes, true) + "]");
     
    482525        byte hash[] = digest.digest(infoBytes);
    483526        if (_log.shouldLog(Log.DEBUG))
    484             _log.debug("info hash: [" + net.i2p.data.Base64.encode(hash) + "]");
     527            _log.debug("info hash: " + I2PSnarkUtil.toHex(hash));
    485528        return hash;
    486529      }
     
    491534  }
    492535
    493  
     536  /** @since 0.8.5 */
     537  public static void main(String[] args) {
     538      if (args.length <= 0) {
     539          System.err.println("Usage: MetaInfo files...");
     540          return;
     541      }
     542      for (int i = 0; i < args.length; i++) {
     543          InputStream in = null;
     544          try {
     545              in = new FileInputStream(args[i]);
     546              MetaInfo meta = new MetaInfo(in);
     547              System.out.println(args[i] + " InfoHash: " + I2PSnarkUtil.toHex(meta.getInfoHash()));
     548          } catch (IOException ioe) {
     549              System.err.println("Error in file " + args[i] + ": " + ioe);
     550          } finally {
     551              try { if (in != null) in.close(); } catch (IOException ioe) {}
     552          }
     553      }
     554  }
    494555}
  • apps/i2psnark/java/src/org/klomp/snark/PeerCoordinator.java

    r0e854623 rf9b2c0bc  
    784784   * Returns a byte array containing the requested piece or null of
    785785   * the piece is unknown.
     786   *
     787   * @throws RuntimeException on IOE getting the data
    786788   */
    787789  public byte[] gotRequest(Peer peer, int piece, int off, int len)
     
    799801      {
    800802        snark.stopTorrent();
    801         _log.error("Error reading the storage for " + metainfo.getName(), ioe);
    802         throw new RuntimeException("B0rked");
     803        String msg = "Error reading the storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
     804        _log.error(msg, ioe);
     805        SnarkManager.instance().addMessage(msg);
     806        SnarkManager.instance().addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
     807        throw new RuntimeException(msg, ioe);
    803808      }
    804809  }
     
    830835   * In that case the peer that supplied the piece should probably be
    831836   * blacklisted.
     837   *
     838   * @throws RuntimeException on IOE saving the piece
    832839   */
    833840  public boolean gotPiece(Peer peer, int piece, byte[] bs)
     
    873880          {
    874881            snark.stopTorrent();
    875             _log.error("Error writing storage for " + metainfo.getName(), ioe);
    876             throw new RuntimeException("B0rked");
     882            String msg = "Error writing storage (piece " + piece + ") for " + metainfo.getName() + ": " + ioe;
     883            _log.error(msg, ioe);
     884            SnarkManager.instance().addMessage(msg);
     885            SnarkManager.instance().addMessage("Fatal storage error: Stopping torrent " + metainfo.getName());
     886            throw new RuntimeException(msg, ioe);
    877887          }
    878888        wantedPieces.remove(p);
  • apps/i2psnark/java/src/org/klomp/snark/Snark.java

    r0e854623 rf9b2c0bc  
    4040import net.i2p.data.Destination;
    4141import net.i2p.util.I2PThread;
    42 
    43 import org.klomp.snark.bencode.BDecoder;
    4442
    4543/**
     
    361359            }
    362360          }
    363         meta = new MetaInfo(new BDecoder(in));
     361        meta = new MetaInfo(in);
    364362        infoHash = meta.getInfoHash();
    365363      }
     
    590588    Storage st = storage;
    591589    if (st != null) {
    592         boolean changed = storage.changed;
     590        boolean changed = storage.isChanged();
    593591        try {
    594592            storage.close();
     
    10141012    //  t.printStackTrace();
    10151013    stopTorrent();
    1016     throw new RuntimeException(s + (t == null ? "" : ": " + t));
     1014    throw new RuntimeException(s, t);
    10171015  }
    10181016
     
    11121110    allChecked = true;
    11131111    checking = false;
    1114     if (storage.changed && completeListener != null)
     1112    if (storage.isChanged() && completeListener != null)
    11151113        completeListener.updateStatus(this);
    11161114  }
  • apps/i2psnark/java/src/org/klomp/snark/SnarkManager.java

    r0e854623 rf9b2c0bc  
    573573
    574574                try {
     575                    // This is somewhat wasteful as this metainfo is thrown away,
     576                    // the real one is created in the Snark constructor.
     577                    // TODO: Make a Snark constructor where we pass the MetaInfo in as a parameter.
    575578                    MetaInfo info = new MetaInfo(fis);
    576579                    try {
     
    623626                } catch (OutOfMemoryError oom) {
    624627                    addMessage(_("ERROR - Out of memory, cannot create torrent from {0}", sfile.getName()) + ": " + oom.getMessage());
     628                    return;
    625629                } finally {
    626630                    if (fis != null) try { fis.close(); } catch (IOException ioe) {}
  • apps/i2psnark/java/src/org/klomp/snark/Storage.java

    r0e854623 rf9b2c0bc  
    5454  private boolean _probablyComplete;  // use this to decide whether to open files RO
    5555
    56   // XXX - Not always set correctly
    57   int piece_size;
    58   int pieces;
    59   boolean changed;
     56  private final int piece_size;
     57  private final int pieces;
     58  private final long total_length;
     59  private boolean changed;
    6060
    6161  /** The default piece size. */
     
    8282    _probablyComplete = false;
    8383    bitfield = new BitField(needed);
     84    piece_size = metainfo.getPieceLength(0);
     85    pieces = needed;
     86    total_length = metainfo.getTotalLength();
    8487  }
    8588
     
    109112      }
    110113
    111     piece_size = MIN_PIECE_SIZE;
    112     pieces = (int) ((total - 1)/piece_size) + 1;
    113     while (pieces > MAX_PIECES && piece_size < MAX_PIECE_SIZE)
    114       {
    115         piece_size = piece_size*2;
    116         pieces = (int) ((total - 1)/piece_size) +1;
    117       }
    118 
    119     // Note that piece_hashes and the bitfield will be filled after
    120     // the MetaInfo is created.
    121     byte[] piece_hashes = new byte[20*pieces];
     114    int pc_size = MIN_PIECE_SIZE;
     115    int pcs = (int) ((total - 1)/pc_size) + 1;
     116    while (pcs > MAX_PIECES && pc_size < MAX_PIECE_SIZE)
     117      {
     118        pc_size *= 2;
     119        pcs = (int) ((total - 1)/pc_size) +1;
     120      }
     121    piece_size = pc_size;
     122    pieces = pcs;
     123    total_length = total;
     124
    122125    bitfield = new BitField(pieces);
    123126    needed = 0;
     
    143146      }
    144147
    145     // Note that the piece_hashes are not correctly setup yet.
     148    byte[] piece_hashes = fast_digestCreate();
    146149    metainfo = new MetaInfo(announce, baseFile.getName(), null, files,
    147150                            lengthsList, piece_size, piece_hashes, total);
     
    149152  }
    150153
    151   // Creates piece hashes for a new storage.
    152   // This does NOT create the files, just the hashes
    153   public void create() throws IOException
    154   {
    155 //    if (true) {
    156         fast_digestCreate();
    157 //    } else {
    158 //        orig_digestCreate();
    159 //    }
    160   }
    161  
    162 /*
    163   private void orig_digestCreate() throws IOException {
     154  /**
     155   * Creates piece hashes for a new storage.
     156   * This does NOT create the files, just the hashes.
     157   * Also sets all the bitfield bits.
     158   *
     159   *  FIXME we can run out of fd's doing this,
     160   *  maybe some sort of global close-RAF-right-away flag
     161   *  would do the trick
     162   */
     163  private byte[] fast_digestCreate() throws IOException {
    164164    // Calculate piece_hashes
    165     MessageDigest digest = null;
    166     try
    167       {
    168         digest = MessageDigest.getInstance("SHA");
    169       }
    170     catch(NoSuchAlgorithmException nsa)
    171       {
    172         throw new InternalError(nsa.toString());
    173       }
    174 
    175     byte[] piece_hashes = metainfo.getPieceHashes();
     165    SHA1 digest = new SHA1();
     166
     167    byte[] piece_hashes = new byte[20 * pieces];
    176168
    177169    byte[] piece = new byte[piece_size];
     
    181173        digest.update(piece, 0, length);
    182174        byte[] hash = digest.digest();
    183         for (int j = 0; j < 20; j++)
    184           piece_hashes[20 * i + j] = hash[j];
    185 
     175        System.arraycopy(hash, 0, piece_hashes, 20 * i, 20);
    186176        bitfield.set(i);
    187 
    188         if (listener != null)
    189           listener.storageChecked(this, i, true);
    190       }
    191 
    192     if (listener != null)
    193       listener.storageAllChecked(this);
    194 
    195     // Reannounce to force recalculating the info_hash.
    196     metainfo = metainfo.reannounce(metainfo.getAnnounce());
    197   }
    198 */
    199 
    200   /** FIXME we can run out of fd's doing this,
    201    *  maybe some sort of global close-RAF-right-away flag
    202    *  would do the trick */
    203   private void fast_digestCreate() throws IOException {
    204     // Calculate piece_hashes
    205     SHA1 digest = new SHA1();
    206 
    207     byte[] piece_hashes = metainfo.getPieceHashes();
    208 
    209     byte[] piece = new byte[piece_size];
    210     for (int i = 0; i < pieces; i++)
    211       {
    212         int length = getUncheckedPiece(i, piece);
    213         digest.update(piece, 0, length);
    214         byte[] hash = digest.digest();
    215         for (int j = 0; j < 20; j++)
    216           piece_hashes[20 * i + j] = hash[j];
    217 
    218         bitfield.set(i);
    219       }
    220 
    221     // Reannounce to force recalculating the info_hash.
    222     metainfo = metainfo.reannounce(metainfo.getAnnounce());
     177      }
     178    return piece_hashes;
    223179  }
    224180
     
    293249  {
    294250    return needed == 0;
     251  }
     252
     253  /**
     254   *  Has the storage changed since instantiation?
     255   *  @since 0.8.5
     256   */
     257  public boolean isChanged() {
     258      return changed;
    295259  }
    296260
     
    316280              if (complete())
    317281                  return 0;
    318               int psz = metainfo.getPieceLength(0);
     282              int psz = piece_size;
    319283              long start = bytes;
    320284              long end = start + lengths[i];
     
    323287              if (!bitfield.get(pc))
    324288                  rv = Math.min(psz - (start % psz), lengths[i]);
    325               int pieces = metainfo.getPieces();
    326289              for (int j = pc + 1; (((long)j) * psz) < end && j < pieces; j++) {
    327290                  if (!bitfield.get(j)) {
     
    419382      long pcEnd = -1;
    420383      long fileEnd = lengths[0] - 1;
    421       int psz = metainfo.getPieceLength(0);
     384      int psz = piece_size;
    422385      for (int i = 0; i < rv.length; i++) {
    423386          pcEnd += psz;
     
    470433    boolean useSavedBitField = savedTime > 0 && savedBitField != null;
    471434
    472     List files = metainfo.getFiles();
     435    List<List<String>> files = metainfo.getFiles();
    473436    if (files == null)
    474437      {
     
    501464          throw new IOException("Could not create directory " + base);
    502465
    503         List  ls = metainfo.getLengths();
     466        List<Long> ls = metainfo.getLengths();
    504467        int size = files.size();
    505468        long total = 0;
     
    512475        for (int i = 0; i < size; i++)
    513476          {
    514             File f = createFileFromNames(base, (List)files.get(i));
    515             lengths[i] = ((Long)ls.get(i)).longValue();
     477            List<String> path = files.get(i);
     478            File f = createFileFromNames(base, path);
     479            // dup file name check after filtering
     480            for (int j = 0; j < i; j++) {
     481                if (f.equals(RAFfile[j])) {
     482                    // Rename and start the check over again
     483                    // Copy path since metainfo list is unmodifiable
     484                    path = new ArrayList(path);
     485                    int last = path.size() - 1;
     486                    String lastPath = path.get(last);
     487                    int dot = lastPath.lastIndexOf('.');
     488                    // foo.mp3 -> foo_.mp3; foo -> _foo
     489                    if (dot >= 0)
     490                        lastPath = lastPath.substring(0, dot) + '_' + lastPath.substring(dot);
     491                    else
     492                        lastPath = '_' + lastPath;
     493                    path.set(last, lastPath);
     494                    f = createFileFromNames(base, path);
     495                    j = 0;
     496                }
     497            }
     498            lengths[i] = ls.get(i).longValue();
    516499            RAFlock[i] = new Object();
    517500            RAFfile[i] = f;
     
    552535
    553536  /**
    554    * Reopen the file descriptors for a restart
    555    * Do existence check but no length check or data reverification
     537   * Doesn't really reopen the file descriptors for a restart.
     538   * Just does an existence check but no length check or data reverification
     539   *
     540   * @param rootDir ignored
     541   * @throws IOE on fail
    556542   */
    557543  public void reopen(String rootDir) throws IOException
    558544  {
    559     File base = new File(rootDir, filterName(metainfo.getName()));
    560 
    561     List files = metainfo.getFiles();
    562     if (files == null)
    563       {
    564         // Reopen base as file.
    565         _util.debug("Reopening file: " + base, Snark.NOTICE);
    566         if (!base.exists())
    567           throw new IOException("Could not reopen file " + base);
    568       }
    569     else
    570       {
    571         // Reopen base as dir.
    572         _util.debug("Reopening directory: " + base, Snark.NOTICE);
    573         if (!base.isDirectory())
    574           throw new IOException("Could not reopen directory " + base);
    575 
    576         int size = files.size();
    577         for (int i = 0; i < size; i++)
    578           {
    579             File f = getFileFromNames(base, (List)files.get(i));
    580             if (!f.exists())
    581                 throw new IOException("Could not reopen file " + f);
    582           }
    583 
     545      if (RAFfile == null)
     546          throw new IOException("Storage not checked yet");
     547      for (int i = 0; i < RAFfile.length; i++) {
     548          if (!RAFfile[i].exists())
     549              throw new IOException("File does not exist: " + RAFfile[i]);
    584550      }
    585551  }
     
    610576  }
    611577
    612   private File createFileFromNames(File base, List names) throws IOException
     578  /**
     579   *  Note that filtering each path element individually may lead to
     580   *  things going in the wrong place if there are duplicates
     581   *  in intermediate path elements after filtering.
     582   */
     583  private static File createFileFromNames(File base, List<String> names) throws IOException
    613584  {
    614585    File f = null;
    615     Iterator it = names.iterator();
     586    Iterator<String> it = names.iterator();
    616587    while (it.hasNext())
    617588      {
    618         String name = filterName((String)it.next());
     589        String name = filterName(it.next());
    619590        if (it.hasNext())
    620591          {
     
    636607  }
    637608
    638   public static File getFileFromNames(File base, List names)
    639   {
    640     Iterator it = names.iterator();
     609  public static File getFileFromNames(File base, List<String> names)
     610  {
     611    Iterator<String> it = names.iterator();
    641612    while (it.hasNext())
    642613      {
    643         String name = filterName((String)it.next());
     614        String name = filterName(it.next());
    644615        base = new File(base, name);
    645616      }
     
    691662          }
    692663        } else {
    693           _util.debug("File '" + names[i] + "' exists, but has wrong length - repairing corruption", Snark.ERROR);
     664          String msg = "File '" + names[i] + "' exists, but has wrong length (expected " +
     665                       lengths[i] + " but found " + length + ") - repairing corruption";
     666          SnarkManager.instance().addMessage(msg);
     667          _util.debug(msg, Snark.ERROR);
    694668          changed = true;
    695669          _probablyComplete = false; // to force RW
     
    707681    if (resume)
    708682      {
    709         pieces = metainfo.getPieces();
    710         byte[] piece = new byte[metainfo.getPieceLength(0)];
     683        byte[] piece = new byte[piece_size];
    711684        int file = 0;
    712685        long fileEnd = lengths[0];
     
    776749    if (listener != null)
    777750        listener.storageCreateFile(this, names[nr], lengths[nr]);
    778     final int ZEROBLOCKSIZE = metainfo.getPieceLength(0);
     751    final int ZEROBLOCKSIZE = piece_size;
    779752    byte[] zeros;
    780753    try {
     
    869842
    870843    // Early typecast, avoid possibly overflowing a temp integer
    871     long start = (long) piece * (long) metainfo.getPieceLength(0);
     844    long start = (long) piece * (long) piece_size;
    872845    int i = 0;
    873846    long raflen = lengths[i];
     
    936909  }
    937910
     911  /**
     912   *  This is a dup of MetaInfo.getPieceLength() but we need it
     913   *  before the MetaInfo is created in our second constructor.
     914   *  @since 0.8.5
     915   */
     916  private int getPieceLength(int piece) {
     917    if (piece >= 0 && piece < pieces -1)
     918      return piece_size;
     919    else if (piece == pieces -1)
     920      return (int)(total_length - ((long)piece * piece_size));
     921    else
     922      throw new IndexOutOfBoundsException("no piece: " + piece);
     923  }
     924
    938925  private int getUncheckedPiece(int piece, byte[] bs)
    939926    throws IOException
    940927  {
    941       return getUncheckedPiece(piece, bs, 0, metainfo.getPieceLength(piece));
     928      return getUncheckedPiece(piece, bs, 0, getPieceLength(piece));
    942929  }
    943930
     
    948935
    949936    // Early typecast, avoid possibly overflowing a temp integer
    950     long start = ((long) piece * (long) metainfo.getPieceLength(0)) + off;
     937    long start = ((long) piece * (long) piece_size) + off;
    951938
    952939    int i = 0;
  • apps/i2psnark/java/src/org/klomp/snark/bencode/BDecoder.java

    r0e854623 rf9b2c0bc  
    6161
    6262  // Used for ugly hack to get SHA hash over the metainfo info map
    63   private String special_map = "info";
     63  private final String special_map = "info";
    6464  private boolean in_special_map = false;
    65   private final MessageDigest sha_digest;
    66 
    67   // Ugly hack. Return the SHA has over bytes that make up the special map.
     65  /** creation deferred until we encounter the special map, to make processing of announce replies more efficient */
     66  private MessageDigest sha_digest;
     67
     68  /**
     69   * Ugly hack. Return the SHA has over bytes that make up the special map.
     70   * @return null if there was no special map
     71   */
    6872  public byte[] get_special_map_digest()
    6973  {
     74    if (sha_digest == null)
     75        return null;
    7076    byte[] result = sha_digest.digest();
    7177    return result;
    7278  }
    7379
     80/****
    7481  // Ugly hack. Name defaults to "info".
    7582  public void set_special_map_name(String name)
     
    7784    special_map = name;
    7885  }
     86****/
    7987
    8088  /**
     
    8593  {
    8694    this.in = in;
    87     // XXX - Used for ugly hack.
    88     try
    89       {
    90         sha_digest = MessageDigest.getInstance("SHA");
    91       }
    92     catch(NoSuchAlgorithmException nsa)
    93       {
    94         throw new InternalError(nsa.toString());
    95       }
    9695  }
    9796
     
    111110  {
    112111    return new BDecoder(in).bdecode();
     112  }
     113
     114  /**
     115   *  Used for SHA1 hack
     116   *  @since 0.8.5
     117   */
     118  private void createDigest() {
     119      if (sha_digest == null) {
     120          try {
     121              sha_digest = MessageDigest.getInstance("SHA");
     122          } catch(NoSuchAlgorithmException nsa) {
     123              throw new InternalError(nsa.toString());
     124          }
     125      } else {
     126          // there are two info maps, but not one inside the other,
     127          // the resulting hash will be incorrect
     128          // throw something? - no, the check in the MetaInfo constructor will catch it.
     129      }
    113130  }
    114131
     
    295312
    296313        // XXX ugly hack
    297         boolean special = special_map.equals(key);
    298         if (special)
     314        // This will not screw up if an info map contains an info map,
     315        // but it will if there are two info maps (not one inside the other)
     316        boolean special = (!in_special_map) && special_map.equals(key);
     317        if (special) {
     318          createDigest();
    299319          in_special_map = true;
     320        }
    300321
    301322        BEValue value = bdecode();
  • apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java

    r0e854623 rf9b2c0bc  
    644644                        // it shouldn't be THAT bad, so keep it in this thread.
    645645                        Storage s = new Storage(_manager.util(), baseFile, announceURL, null);
    646                         s.create();
    647646                        s.close(); // close the files... maybe need a way to pass this Storage to addTorrent rather than starting over
    648647                        MetaInfo info = s.getMetaInfo();
     
    19691968                try {
    19701969                    in = new FileInputStream(file);
    1971                     // we do not retain this MetaInfo object, hopefully it will go away quickly
    1972                     MetaInfo info = new MetaInfo(in);
     1970                    byte[] fileInfoHash = new byte[20];
     1971                    String name = MetaInfo.getNameAndInfoHash(in, fileInfoHash);
    19731972                    try { in.close(); } catch (IOException ioe) {}
    1974                     Snark snark = _manager.getTorrentByInfoHash(info.getInfoHash());
     1973                    Snark snark = _manager.getTorrentByInfoHash(fileInfoHash);
    19751974                    if (snark != null) {
    19761975                        _manager.addMessage(_("Torrent with this info hash is already running: {0}", snark.getBaseName()));
     
    19781977                    }
    19791978
    1980                     String name = info.getName();
    19811979                    name = Storage.filterName(name);
    19821980                    name = name + ".torrent";
Note: See TracChangeset for help on using the changeset viewer.