Changeset 30ccf1b


Ignore:
Timestamp:
Dec 22, 2013 1:52:35 PM (6 years ago)
Author:
zzz <zzz@…>
Branches:
master
Children:
a2e7fa8
Parents:
01b1534
Message:

i2psnark:

  • Refactor file deletion in prep for better file name handling (ticket #571)
  • Don't use canonical files in directory listings, for speed and to avoid file comparison problems (tickets #1079, #1148)
  • Set base file/dir in Storage constructor, make final, in prep for arbitrary locations (ticket #1028)
Location:
apps/i2psnark/java/src/org/klomp/snark
Files:
3 edited

Legend:

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

    r01b1534 r30ccf1b  
    224224  private ConnectionAcceptor acceptor;
    225225  private TrackerClient trackerclient;
    226   private String rootDataDir = ".";
     226  private final File rootDataDir;
    227227  private final CompleteListener completeListener;
    228228  private volatile boolean stopped;
     
    292292
    293293    this.torrent = torrent;
    294     this.rootDataDir = rootDir;
     294    this.rootDataDir = new File(rootDir);
    295295
    296296    stopped = true;
     
    396396          {
    397397            activity = "Checking storage";
    398             storage = new Storage(_util, meta, slistener);
     398            storage = new Storage(_util, rootDataDir, meta, slistener);
    399399            if (completeListener != null) {
    400                 storage.check(rootDataDir,
    401                               completeListener.getSavedTorrentTime(this),
     400                storage.check(completeListener.getSavedTorrentTime(this),
    402401                              completeListener.getSavedTorrentBitField(this));
    403402            } else {
    404                 storage.check(rootDataDir);
     403                storage.check();
    405404            }
    406405            // have to figure out when to reopen
     
    454453    this.infoHash = ih;
    455454    this.additionalTrackerURL = trackerURL;
    456     this.rootDataDir = rootDir;
     455    this.rootDataDir = new File(rootDir);
    457456    stopped = true;
    458457    id = generateID();
     
    549548        if (storage != null) {
    550549            try {
    551                  storage.reopen(rootDataDir);
     550                 storage.reopen();
    552551             }   catch (IOException ioe) {
    553552                 try { storage.close(); } catch (IOException ioee) {
     
    11051104      try {
    11061105          // The following two may throw IOE...
    1107           storage = new Storage(_util, metainfo, this);
    1108           storage.check(rootDataDir);
     1106          storage = new Storage(_util, rootDataDir, metainfo, this);
     1107          storage.check();
    11091108          // ... so don't set meta until here
    11101109          meta = metainfo;
  • apps/i2psnark/java/src/org/klomp/snark/Storage.java

    r01b1534 r30ccf1b  
    3333import java.util.List;
    3434import java.util.Map;
     35import java.util.SortedSet;
    3536import java.util.StringTokenizer;
     37import java.util.TreeSet;
    3638import java.util.concurrent.ConcurrentHashMap;
    3739import java.util.concurrent.atomic.AtomicInteger;
     
    5355  private final MetaInfo metainfo;
    5456  private final List<TorrentFile> _torrentFiles;
     57  private final File _base;
    5558  private final StorageListener listener;
    5659  private final I2PSnarkUtil _util;
     
    8992   * Does not check storage. Caller MUST call check()
    9093   */
    91   public Storage(I2PSnarkUtil util, MetaInfo metainfo, StorageListener listener)
     94  public Storage(I2PSnarkUtil util, File rootDir, MetaInfo metainfo, StorageListener listener)
    9295  {
    9396    _util = util;
    9497    _log = util.getContext().logManager().getLog(Storage.class);
     98    boolean areFilesPublic = _util.getFilesPublic();
     99    if (areFilesPublic)
     100        _base = new File(rootDir, filterName(metainfo.getName()));
     101    else
     102        _base = new SecureFile(rootDir, filterName(metainfo.getName()));
    95103    this.metainfo = metainfo;
    96104    this.listener = listener;
     
    122130  {
    123131    _util = util;
     132    _base = baseFile;
    124133    _log = util.getContext().logManager().getLog(Storage.class);
    125134    this.listener = listener;
     
    306315
    307316  /**
    308    *  @param file canonical path (non-directory)
     317   *  @param file non-canonical path (non-directory)
    309318   *  @return number of bytes remaining; -1 if unknown file
    310319   *  @since 0.7.14
    311320   */
    312   public long remaining(String file) {
     321  public long remaining(File file) {
    313322      long bytes = 0;
    314323      for (TorrentFile tf : _torrentFiles) {
    315324          File f = tf.RAFfile;
    316           // use canonical in case snark dir or sub dirs are symlinked
    317           String canonical = null;
    318           if (f != null) {
    319               try {
    320                   canonical = f.getCanonicalPath();
    321               } catch (IOException ioe) {
    322                   f = null;
    323               }
    324           }
    325           if (f != null && canonical.equals(file)) {
     325          if (f.equals(file)) {
    326326              if (complete())
    327327                  return 0;
     
    349349
    350350  /**
    351    *  @param file canonical path (non-directory)
     351   *  @param file non-canonical path (non-directory)
    352352   *  @since 0.8.1
    353353   */
    354   public int getPriority(String file) {
     354  public int getPriority(File file) {
    355355      if (complete() || metainfo.getFiles() == null)
    356356          return 0;
    357357      for (TorrentFile tf : _torrentFiles) {
    358358          File f = tf.RAFfile;
    359           // use canonical in case snark dir or sub dirs are symlinked
    360           if (f != null) {
    361               try {
    362                   String canonical = f.getCanonicalPath();
    363                   if (canonical.equals(file))
    364                       return tf.priority;
    365               } catch (IOException ioe) {}
    366           }
     359          if (f.equals(file))
     360              return tf.priority;
    367361      }
    368362      return 0;
     
    372366   *  Must call Snark.updatePiecePriorities()
    373367   *  (which calls getPiecePriorities()) after calling this.
    374    *  @param file canonical path (non-directory)
     368   *  @param file non-canonical path (non-directory)
    375369   *  @param pri default 0; <0 to disable
    376370   *  @since 0.8.1
    377371   */
    378   public void setPriority(String file, int pri) {
     372  public void setPriority(File file, int pri) {
    379373      if (complete() || metainfo.getFiles() == null)
    380374          return;
    381375      for (TorrentFile tf : _torrentFiles) {
    382376          File f = tf.RAFfile;
    383           // use canonical in case snark dir or sub dirs are symlinked
    384           if (f != null) {
    385               try {
    386                   String canonical = f.getCanonicalPath();
    387                   if (canonical.equals(file)) {
    388                       tf.priority = pri;
    389                       return;
    390                   }
    391               } catch (IOException ioe) {}
     377          if (f.equals(file)) {
     378              tf.priority = pri;
     379              return;
    392380          }
    393381      }
     
    491479   * Only call this once, and only after the constructor with the metainfo.
    492480   */
    493   public void check(String rootDir) throws IOException
    494   {
    495     check(rootDir, 0, null);
     481  public void check() throws IOException
     482  {
     483    check(0, null);
    496484  }
    497485
     
    501489   * Only call this once, and only after the constructor with the metainfo.
    502490   */
    503   public void check(String rootDir, long savedTime, BitField savedBitField) throws IOException
    504   {
    505     File base;
     491  public void check(long savedTime, BitField savedBitField) throws IOException
     492  {
    506493    boolean areFilesPublic = _util.getFilesPublic();
    507     if (areFilesPublic)
    508         base = new File(rootDir, filterName(metainfo.getName()));
    509     else
    510         base = new SecureFile(rootDir, filterName(metainfo.getName()));
    511494    boolean useSavedBitField = savedTime > 0 && savedBitField != null;
    512495
     
    518501        // Create base as file.
    519502        if (_log.shouldLog(Log.INFO))
    520             _log.info("Creating/Checking file: " + base);
    521         if (!base.createNewFile() && !base.exists())
    522           throw new IOException("Could not create file " + base);
    523 
    524         _torrentFiles.add(new TorrentFile(base, base, metainfo.getTotalLength()));
     503            _log.info("Creating/Checking file: " + _base);
     504        if (!_base.createNewFile() && !_base.exists())
     505          throw new IOException("Could not create file " + _base);
     506
     507        _torrentFiles.add(new TorrentFile(_base, _base, metainfo.getTotalLength()));
    525508        if (useSavedBitField) {
    526             long lm = base.lastModified();
     509            long lm = _base.lastModified();
    527510            if (lm <= 0 || lm > savedTime)
    528511                useSavedBitField = false;
    529             else if (base.length() != metainfo.getTotalLength())
     512            else if (_base.length() != metainfo.getTotalLength())
    530513                useSavedBitField = false;
    531514        }
     
    535518        // Create base as dir.
    536519        if (_log.shouldLog(Log.INFO))
    537             _log.info("Creating/Checking directory: " + base);
    538         if (!base.mkdir() && !base.isDirectory())
    539           throw new IOException("Could not create directory " + base);
     520            _log.info("Creating/Checking directory: " + _base);
     521        if (!_base.mkdir() && !_base.isDirectory())
     522          throw new IOException("Could not create directory " + _base);
    540523
    541524        List<Long> ls = metainfo.getLengths();
     
    545528          {
    546529            List<String> path = files.get(i);
    547             File f = createFileFromNames(base, path, areFilesPublic);
     530            File f = createFileFromNames(_base, path, areFilesPublic);
    548531            // dup file name check after filtering
    549532            for (int j = 0; j < i; j++) {
     
    561544                        lastPath = '_' + lastPath;
    562545                    path.set(last, lastPath);
    563                     f = createFileFromNames(base, path, areFilesPublic);
     546                    f = createFileFromNames(_base, path, areFilesPublic);
    564547                    j = 0;
    565548                }
    566549            }
    567550            long len = ls.get(i).longValue();
    568             _torrentFiles.add(new TorrentFile(base, f, len));
     551            _torrentFiles.add(new TorrentFile(_base, f, len));
    569552            total += len;
    570553            if (useSavedBitField) {
     
    613596   * @throws IOE on fail
    614597   */
    615   public void reopen(String rootDir) throws IOException
     598  public void reopen() throws IOException
    616599  {
    617600      if (_torrentFiles.isEmpty())
     
    689672   *  things going in the wrong place if there are duplicates
    690673   *  in intermediate path elements after filtering.
     674   *
     675   *  @param names path elements
    691676   */
    692677  private static File createFileFromNames(File base, List<String> names, boolean areFilesPublic) throws IOException
     
    722707  }
    723708
    724   public static File getFileFromNames(File base, List<String> names)
    725   {
    726     Iterator<String> it = names.iterator();
    727     while (it.hasNext())
    728       {
    729         String name = filterName(it.next());
    730         base = new File(base, name);
    731       }
    732     return base;
     709  /**
     710   *  The base file or directory.
     711   *  @return a new List
     712   */
     713  public File getBase() {
     714      return _base;
     715  }
     716
     717  /**
     718   *  Does not include directories. Unsorted.
     719   *  @since 0.9.10
     720   *  @return a new List
     721   */
     722  public List<File> getFiles() {
     723      List<File> rv = new ArrayList<File>(_torrentFiles.size());
     724      for (TorrentFile tf : _torrentFiles) {
     725          rv.add(tf.RAFfile);
     726      }
     727      return rv;
     728  }
     729
     730  /**
     731   *  Includes the base for a multi-file torrent.
     732   *  Sorted bottom-up for easy deletion.
     733   *  Slow. Use for deletion only.
     734   *  @since 0.9.10
     735   *  @return a new Set or null for a single-file torrent
     736   */
     737  public SortedSet<File> getDirectories() {
     738      if (!_base.isDirectory())
     739          return null;
     740      SortedSet<File> rv = new TreeSet<File>(Collections.reverseOrder());
     741      rv.add(_base);
     742      for (TorrentFile tf : _torrentFiles) {
     743          File f = tf.RAFfile;
     744          do {
     745              f = f.getParentFile();
     746          } while (f != null && rv.add(f));
     747      }
     748      return rv;
    733749  }
    734750
  • apps/i2psnark/java/src/org/klomp/snark/web/I2PSnarkServlet.java

    r01b1534 r30ccf1b  
    1818import java.util.Set;
    1919import java.util.TreeMap;
    20 import java.util.TreeSet;
    2120
    2221import javax.servlet.ServletConfig;
     
    829828                                break;
    830829                            }
     830                            Storage storage = snark.getStorage();
     831                            if (storage == null)
     832                                break;
    831833                            // step 1 delete files
    832                             for (int i = 0; i < files.size(); i++) {
    833                                 // multifile torrents have the getFiles() return lists of lists of filenames, but
    834                                 // each of those lists just contain a single file afaict...
    835                                 File df = Storage.getFileFromNames(f, files.get(i));
     834                            for (File df : storage.getFiles()) {
    836835                                if (df.delete()) {
    837836                                    //_manager.addMessage(_("Data file deleted: {0}", df.getAbsolutePath()));
     
    840839                                }
    841840                            }
    842                             // step 2 make Set of dirs with reverse sort
    843                             Set<File> dirs = new TreeSet<File>(Collections.reverseOrder());
    844                             for (List<String> list : files) {
    845                                 for (int i = 1; i < list.size(); i++) {
    846                                     dirs.add(Storage.getFileFromNames(f, list.subList(0, i)));
    847                                 }
    848                             }
    849                             // step 3 delete dirs bottom-up
     841                            // step 2 delete dirs bottom-up
     842                            Set<File> dirs = storage.getDirectories();
     843                            if (_log.shouldLog(Log.INFO))
     844                                _log.info("Dirs to delete: " + DataHelper.toString(dirs));
     845                            boolean ok = false;
    850846                            for (File df : dirs) {
    851847                                if (df.delete()) {
     848                                    ok = true;
    852849                                    //_manager.addMessage(_("Data dir deleted: {0}", df.getAbsolutePath()));
    853850                                } else {
     851                                    ok = false;
    854852                                    _manager.addMessage(_("Directory could not be deleted: {0}", df.getAbsolutePath()));
    855853                                    if (_log.shouldLog(Log.WARN))
     
    857855                                }
    858856                            }
    859                             // step 4 delete base
    860                             if (f.delete()) {
    861                                 _manager.addMessage(_("Directory deleted: {0}", f.getAbsolutePath()));
    862                             } else {
    863                                 _manager.addMessage(_("Directory could not be deleted: {0}", f.getAbsolutePath()));
    864                                 if (_log.shouldLog(Log.WARN))
    865                                     _log.warn("Could not delete dir " + f);
    866                             }
     857                            // step 3 message for base (last one)
     858                            if (ok)
     859                                _manager.addMessage(_("Directory deleted: {0}", storage.getBase()));
    867860                            break;
    868861                        }
     
    24632456            String status = "";
    24642457            long length = item.length();
     2458            int priority = 0;
    24652459            if (item.isDirectory()) {
    24662460                complete = true;
     
    24732467                } else {
    24742468                    Storage storage = snark.getStorage();
    2475                     try {
    2476                         File f = item;
    2477                             long remaining = storage.remaining(f.getCanonicalPath());
     2469
     2470                            long remaining = storage.remaining(item);
    24782471                            if (remaining < 0) {
    24792472                                complete = true;
     
    24832476                                status = toImg("tick") + ' ' + _("Complete");
    24842477                            } else {
    2485                                 int priority = storage.getPriority(f.getCanonicalPath());
     2478                                priority = storage.getPriority(item);
    24862479                                if (priority < 0)
    24872480                                    status = toImg("cancel");
     
    24942487                                         " (" + DataHelper.formatSize2(remaining) + "B " + _("remaining") + ")";
    24952488                            }
    2496                     } catch (IOException ioe) {
    2497                         status = "Not a file? " + ioe;
    2498                     }
     2489
    24992490                }
    25002491            }
     
    25352526            if (showPriority) {
    25362527                buf.append("<td class=\"priority\">");
    2537                 File f = item;
    25382528                if ((!complete) && (!item.isDirectory())) {
    2539                     int pri = snark.getStorage().getPriority(f.getCanonicalPath());
    2540                     buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
    2541                     if (pri > 0)
     2529                    buf.append("<input type=\"radio\" value=\"5\" name=\"pri.").append(item).append("\" ");
     2530                    if (priority > 0)
    25422531                        buf.append("checked=\"true\"");
    25432532                    buf.append('>').append(_("High"));
    25442533
    2545                     buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
    2546                     if (pri == 0)
     2534                    buf.append("<input type=\"radio\" value=\"0\" name=\"pri.").append(item).append("\" ");
     2535                    if (priority == 0)
    25472536                        buf.append("checked=\"true\"");
    25482537                    buf.append('>').append(_("Normal"));
    25492538
    2550                     buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(f.getCanonicalPath()).append("\" ");
    2551                     if (pri < 0)
     2539                    buf.append("<input type=\"radio\" value=\"-9\" name=\"pri.").append(item).append("\" ");
     2540                    if (priority < 0)
    25522541                        buf.append("checked=\"true\"");
    25532542                    buf.append('>').append(_("Skip"));
     
    26442633            if (key.startsWith("pri.")) {
    26452634                try {
    2646                     String file = key.substring(4);
     2635                    File file = new File(key.substring(4));
    26472636                    String val = entry.getValue()[0];   // jetty arrays
    26482637                    int pri = Integer.parseInt(val);
Note: See TracChangeset for help on using the changeset viewer.