Kaynağa Gözat

2008-09-07 Tatsuhiro Tsujikawa <tujikawa at rednoah dot com>

	Implemented the ability to get timestamp from remote HTTP server 
and
	apply it to local file. To enable this feature, --remote-time 
option
	is added. No usage text has been written yet.
	If several servers returns difference timestamp, then aria2 uses 
latest
	one.
	* src/CopyDiskAdaptor.cc
	* src/CopyDiskAdaptor.h
	* src/DirectDiskAdaptor.cc
	* src/DirectDiskAdaptor.h
	* src/DiskAdaptor.h
	* src/File.cc
	* src/File.h
	* src/HttpHeader.cc
	* src/HttpHeader.h
	* src/HttpResponse.cc
	* src/HttpResponse.h
	* src/HttpResponseCommand.cc
	* src/HttpResponseCommand.h
	* src/MultiDiskAdaptor.cc
	* src/MultiDiskAdaptor.h
	* src/OptionHandlerFactory.cc
	* src/RequestGroup.cc
	* src/RequestGroup.h
	* src/RequestGroupMan.cc
	* src/option_processing.cc
	* src/prefs.cc
	* src/prefs.h
	* test/CopyDiskAdaptorTest.cc
	* test/FileTest.cc
	* test/Makefile.am
	* test/Makefile.in
	* test/MultiDiskAdaptorTest.cc
	* test/TestUtil.cc
Tatsuhiro Tsujikawa 17 yıl önce
ebeveyn
işleme
dbc8f5b737

+ 36 - 0
ChangeLog

@@ -1,3 +1,39 @@
+2008-09-07  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
+
+	Implemented the ability to get timestamp from remote HTTP server and
+	apply it to local file. To enable this feature, --remote-time option
+	is added. No usage text has been written yet.
+	If several servers returns difference timestamp, then aria2 uses latest
+	one.
+	* src/CopyDiskAdaptor.cc
+	* src/CopyDiskAdaptor.h
+	* src/DirectDiskAdaptor.cc
+	* src/DirectDiskAdaptor.h
+	* src/DiskAdaptor.h
+	* src/File.cc
+	* src/File.h
+	* src/HttpHeader.cc
+	* src/HttpHeader.h
+	* src/HttpResponse.cc
+	* src/HttpResponse.h
+	* src/HttpResponseCommand.cc
+	* src/HttpResponseCommand.h
+	* src/MultiDiskAdaptor.cc
+	* src/MultiDiskAdaptor.h
+	* src/OptionHandlerFactory.cc
+	* src/RequestGroup.cc
+	* src/RequestGroup.h
+	* src/RequestGroupMan.cc
+	* src/option_processing.cc
+	* src/prefs.cc
+	* src/prefs.h
+	* test/CopyDiskAdaptorTest.cc
+	* test/FileTest.cc
+	* test/Makefile.am
+	* test/Makefile.in
+	* test/MultiDiskAdaptorTest.cc
+	* test/TestUtil.cc
+
 2008-09-07  Tatsuhiro Tsujikawa  <tujikawa at rednoah dot com>
 
 	Fixed the bug that DiskWriterEntry is not created when its

+ 17 - 0
src/CopyDiskAdaptor.cc

@@ -37,6 +37,7 @@
 #include "Logger.h"
 #include "Util.h"
 #include "message.h"
+#include "File.h"
 
 namespace aria2 {
 
@@ -70,4 +71,20 @@ std::string CopyDiskAdaptor::getFilePath()
   return storeDir+"/"+tempFilename;
 }
 
+size_t CopyDiskAdaptor::utime(const Time& actime, const Time& modtime)
+{
+  size_t numOK = 0;
+  std::string topDirPath = storeDir+"/"+topDir;
+  for(std::deque<SharedHandle<FileEntry> >::const_iterator i =
+	fileEntries.begin(); i != fileEntries.end(); ++i) {
+    if((*i)->isExtracted() && (*i)->isRequested()) {
+      File f(topDirPath+"/"+(*i)->getPath());
+      if(f.isFile() && f.utime(actime, modtime)) {
+	++numOK;
+      }
+    }
+  }
+  return numOK;
+}
+
 } // namespace aria2

+ 2 - 0
src/CopyDiskAdaptor.h

@@ -54,6 +54,8 @@ public:
 
   virtual void onDownloadComplete();
 
+  virtual size_t utime(const Time& actime, const Time& modtime);
+
   // tempFilename is relative to storeDir
   void setTempFilename(const std::string& tempFilename) {
     this->tempFilename = tempFilename;

+ 12 - 0
src/DirectDiskAdaptor.cc

@@ -34,6 +34,7 @@
 /* copyright --> */
 #include "DirectDiskAdaptor.h"
 #include "FileEntry.h"
+#include "File.h"
 
 namespace aria2 {
 
@@ -48,4 +49,15 @@ void DirectDiskAdaptor::onDownloadComplete()
   openFile();
 }
 
+size_t DirectDiskAdaptor::utime(const Time& actime, const Time& modtime)
+{
+  File f(getFilePath());
+  if(f.isFile() && f.utime(actime, modtime)) {
+    return 1;
+  } else {
+    return 0;
+  }
+}
+
+
 } // namespace aria2

+ 2 - 0
src/DirectDiskAdaptor.h

@@ -47,6 +47,8 @@ public:
   virtual std::string getFilePath();
 
   virtual void onDownloadComplete();
+
+  virtual size_t utime(const Time& actime, const Time& modtime);
 };
 
 typedef SharedHandle<DirectDiskAdaptor> DirectDiskAdaptorHandle;

+ 5 - 0
src/DiskAdaptor.h

@@ -36,6 +36,7 @@
 #define _D_DISK_ADAPTOR_H_
 
 #include "BinaryStream.h"
+#include "TimeA2.h"
 #include <string>
 #include <deque>
 
@@ -103,6 +104,10 @@ public:
   // Call one of openFile/openExistingFile/initAndOpenFile before calling this
   // function.
   virtual void cutTrailingGarbage() = 0;
+
+  // Returns the number of files, the actime and modtime of which are
+  // successfully changed.
+  virtual size_t utime(const Time& actime, const Time& modtime) = 0;
 };
 
 typedef SharedHandle<DiskAdaptor> DiskAdaptorHandle;

+ 19 - 0
src/File.cc

@@ -38,6 +38,8 @@
 #include <cstring>
 #include <stdlib.h>
 #include <deque>
+#include <sys/types.h>
+#include <utime.h>
 
 namespace aria2 {
 
@@ -178,4 +180,21 @@ bool File::renameTo(const std::string& dest)
   }
 }
 
+bool File::utime(const Time& actime, const Time& modtime) const
+{
+  struct utimbuf ub;
+  ub.actime = actime.getTime();
+  ub.modtime = modtime.getTime();
+  return ::utime(name.c_str(), &ub) == 0;
+}
+
+Time File::getModifiedTime()
+{
+  a2_struct_stat fstat;
+  if(fillStat(fstat) < 0) {
+    return 0;
+  }
+  return Time(fstat.st_mtime);
+}
+
 } // namespace aria2

+ 5 - 0
src/File.h

@@ -37,6 +37,7 @@
 
 #include "common.h"
 #include "a2io.h"
+#include "TimeA2.h"
 #include <string>
 
 namespace aria2 {
@@ -102,6 +103,10 @@ public:
   static bool isDir(const std::string& filename);
 
   bool renameTo(const std::string& dest);
+
+  bool utime(const Time& actime, const Time& modtime) const;
+
+  Time getModifiedTime();
 };
 
 } // namespace aria2

+ 2 - 0
src/HttpHeader.cc

@@ -68,6 +68,8 @@ const std::string HttpHeader::CONTENT_LENGTH("Content-Length");
 
 const std::string HttpHeader::CONTENT_RANGE("Content-Range");
 
+const std::string HttpHeader::LAST_MODIFIED("Last-Modified");
+
 const std::string HttpHeader::HTTP_1_1("HTTP/1.1");
 
 const std::string HttpHeader::S200("200");

+ 2 - 0
src/HttpHeader.h

@@ -110,6 +110,8 @@ public:
 
   static const std::string CONTENT_RANGE;
 
+  static const std::string LAST_MODIFIED;
+
   static const std::string HTTP_1_1;
 
   static const std::string S200;

+ 5 - 0
src/HttpResponse.cc

@@ -249,4 +249,9 @@ time_t HttpResponse::getRetryAfter() const
   return httpHeader->getFirstAsUInt(HttpHeader::RETRY_AFTER);
 }
 
+Time HttpResponse::getLastModifiedTime() const
+{
+  return Time::parseHTTPDate(httpHeader->getFirst(HttpHeader::LAST_MODIFIED));
+}
+
 } // namespace aria2

+ 3 - 1
src/HttpResponse.h

@@ -37,7 +37,7 @@
 
 #include "common.h"
 #include "SharedHandle.h"
-#include "a2time.h"
+#include "TimeA2.h"
 #include <stdint.h>
 
 namespace aria2 {
@@ -115,6 +115,8 @@ public:
   bool hasRetryAfter() const;
 
   time_t getRetryAfter() const;
+
+  Time getLastModifiedTime() const;
 };
 
 typedef SharedHandle<HttpResponse> HttpResponseHandle;

+ 13 - 0
src/HttpResponseCommand.cc

@@ -120,6 +120,9 @@ bool HttpResponseCommand::executeInternal()
 	(StringFormat(EX_DUPLICATE_FILE_DOWNLOAD,
 		      _requestGroup->getFilePath().c_str()).str());
     }
+    // update last modified time
+    updateLastModifiedTime(httpResponse->getLastModifiedTime());
+
     if(totalLength == 0 || httpResponse->isTransferEncodingSpecified() ||
        shouldInflateContentEncoding(httpResponse)) {
       // we ignore content-length when transfer-encoding is set
@@ -131,11 +134,21 @@ bool HttpResponseCommand::executeInternal()
   } else {
     // validate totalsize
     _requestGroup->validateTotalLength(httpResponse->getEntityLength());
+    // update last modified time
+    updateLastModifiedTime(httpResponse->getLastModifiedTime());
+
     e->commands.push_back(createHttpDownloadCommand(httpResponse));
     return true;
   }
 }
 
+void HttpResponseCommand::updateLastModifiedTime(const Time& lastModified)
+{
+  if(e->option->getAsBool(PREF_REMOTE_TIME)) {
+    _requestGroup->updateLastModifiedTime(lastModified);
+  }
+}
+
 static bool fileIsGzipped(const SharedHandle<HttpResponse>& httpResponse)
 {
   std::string filename =

+ 3 - 0
src/HttpResponseCommand.h

@@ -37,6 +37,7 @@
 
 #include "AbstractCommand.h"
 #include "Decoder.h"
+#include "TimeA2.h"
 
 namespace aria2 {
 
@@ -59,6 +60,8 @@ private:
 			    = SharedHandle<Decoder>(),
 			    const SharedHandle<Decoder>& contentEncodingDecoder
 			    = SharedHandle<Decoder>());
+
+  void updateLastModifiedTime(const Time& lastModified);
 protected:
   bool executeInternal();
 

+ 15 - 0
src/MultiDiskAdaptor.cc

@@ -480,4 +480,19 @@ void MultiDiskAdaptor::setMaxOpenFiles(size_t maxOpenFiles)
   _maxOpenFiles = maxOpenFiles;
 }
 
+size_t MultiDiskAdaptor::utime(const Time& actime, const Time& modtime)
+{
+  size_t numOK = 0;
+  for(std::deque<SharedHandle<FileEntry> >::const_iterator i =
+	fileEntries.begin(); i != fileEntries.end(); ++i) {
+    if((*i)->isRequested()) {
+      File f(getTopDirPath()+"/"+(*i)->getPath());
+      if(f.isFile() && f.utime(actime, modtime)) {
+	++numOK;
+      }
+    }
+  }
+  return numOK;
+}
+
 } // namespace aria2

+ 2 - 0
src/MultiDiskAdaptor.h

@@ -189,6 +189,8 @@ public:
   virtual void cutTrailingGarbage();
 
   void setMaxOpenFiles(size_t maxOpenFiles);
+
+  virtual size_t utime(const Time& actime, const Time& modtime);
 };
 
 typedef SharedHandle<MultiDiskAdaptor> MultiDiskAdaptorHandle;

+ 1 - 0
src/OptionHandlerFactory.cc

@@ -154,6 +154,7 @@ OptionHandlers OptionHandlerFactory::createOptionHandlers()
 						0, INT32_MAX)));
   handlers.push_back(SH(new DefaultOptionHandler(PREF_SERVER_STAT_IF)));
   handlers.push_back(SH(new DefaultOptionHandler(PREF_SERVER_STAT_OF)));
+  handlers.push_back(SH(new BooleanOptionHandler(PREF_REMOTE_TIME)));
 
   return handlers;
 }

+ 18 - 0
src/RequestGroup.cc

@@ -123,6 +123,7 @@ RequestGroup::RequestGroup(const Option* option,
   _forceHaltRequested(false),
   _singleHostMultiConnectionEnabled(true),
   _uriSelector(new InOrderURISelector()),
+  _lastModifiedTime(-1),
   _option(option),
   _logger(LogFactory::getInstance())
 {
@@ -1025,4 +1026,21 @@ void RequestGroup::setURISelector(const SharedHandle<URISelector>& uriSelector)
   _uriSelector = uriSelector;
 }
 
+void RequestGroup::applyLastModifiedTimeToLocalFiles()
+{
+  if(!_pieceStorage.isNull() && _lastModifiedTime.good()) {
+    time_t t = _lastModifiedTime.getTime();
+    _logger->info("Applying Last-Modified time: %s", ctime(&t));
+    size_t n =
+      _pieceStorage->getDiskAdaptor()->utime(Time(), _lastModifiedTime);
+    _logger->info("Last-Modified attrs of %zu files were updated.", n);
+  }
+}
+void RequestGroup::updateLastModifiedTime(const Time& time)
+{
+  if(time.good() && _lastModifiedTime < time) {
+    _lastModifiedTime = time;
+  }
+}
+
 } // namespace aria2

+ 7 - 0
src/RequestGroup.h

@@ -38,6 +38,7 @@
 #include "common.h"
 #include "SharedHandle.h"
 #include "TransferStat.h"
+#include "TimeA2.h"
 #include <string>
 #include <deque>
 
@@ -115,6 +116,8 @@ private:
 
   SharedHandle<URISelector> _uriSelector;
 
+  Time _lastModifiedTime;
+
   const Option* _option;
 
   Logger* _logger;
@@ -361,6 +364,10 @@ public:
   static const std::string ACCEPT_METALINK;
 
   void setURISelector(const SharedHandle<URISelector>& uriSelector);
+
+  void applyLastModifiedTimeToLocalFiles();
+
+  void updateLastModifiedTime(const Time& time);
 };
 
 typedef SharedHandle<RequestGroup> RequestGroupHandle;

+ 2 - 1
src/RequestGroupMan.cc

@@ -153,8 +153,9 @@ public:
   {
     if(group->getNumCommand() == 0) {
       try {
-	group->closeFile();      
+	group->closeFile();
 	if(group->downloadFinished()) {
+	  group->applyLastModifiedTimeToLocalFiles();
 	  group->reportDownloadFinished();
 	  if(group->allDownloadFinished()) {
 	    group->getProgressInfoFile()->removeFile();

+ 8 - 1
src/option_processing.cc

@@ -158,6 +158,7 @@ Option* createDefaultOption()
   op->put(PREF_LOG_LEVEL, V_DEBUG);
   op->put(PREF_URI_SELECTOR, V_INORDER);
   op->put(PREF_SERVER_STAT_TIMEOUT, "86400");// 1day
+  op->put(PREF_REMOTE_TIME, V_FALSE);
   return op;
 }
 
@@ -239,6 +240,7 @@ Option* option_processing(int argc, char* const argv[])
       { PREF_SERVER_STAT_IF.c_str(), required_argument, &lopt, 221 },
       { PREF_SERVER_STAT_OF.c_str(), required_argument, &lopt, 222 },
       { PREF_SERVER_STAT_TIMEOUT.c_str(), required_argument, &lopt, 223 },
+      { PREF_REMOTE_TIME.c_str(), optional_argument, 0, 'R' },
 #if defined ENABLE_BITTORRENT || defined ENABLE_METALINK
       { PREF_SHOW_FILES.c_str(), no_argument, NULL, 'S' },
       { PREF_SELECT_FILE.c_str(), required_argument, &lopt, 21 },
@@ -280,7 +282,9 @@ Option* option_processing(int argc, char* const argv[])
       { "help", optional_argument, NULL, 'h' },
       { 0, 0, 0, 0 }
     };
-    c = getopt_long(argc, argv, "Dd:o:l:s:pt:m:vh::ST:M:C:a:cU:ni:j:Z::P::q::", longOpts, &optIndex);
+    c = getopt_long(argc, argv, 
+		    "Dd:o:l:s:pt:m:vh::ST:M:C:a:cU:ni:j:Z::P::q::R::",
+		    longOpts, &optIndex);
     if(c == -1) {
       break;
     }
@@ -547,6 +551,9 @@ Option* option_processing(int argc, char* const argv[])
     case 'q':
       cmdstream << PREF_QUIET << "=" << toBoolArg(optarg) << "\n";
       break;
+    case 'R':
+      cmdstream << PREF_REMOTE_TIME << "=" << toBoolArg(optarg) << "\n";
+      break;
     case 'v':
       showVersion();
       exit(EXIT_SUCCESS);

+ 2 - 0
src/prefs.cc

@@ -145,6 +145,8 @@ const std::string PREF_SERVER_STAT_TIMEOUT("server-stat-timeout");
 const std::string PREF_SERVER_STAT_IF("server-stat-if");
 // value: string that your file system recognizes as a file name.
 const std::string PREF_SERVER_STAT_OF("server-stat-of");
+// value: true | false
+const std::string PREF_REMOTE_TIME("remote-time");
 
 /**
  * FTP related preferences

+ 2 - 0
src/prefs.h

@@ -149,6 +149,8 @@ extern const std::string PREF_SERVER_STAT_TIMEOUT;
 extern const std::string PREF_SERVER_STAT_IF;
 // value: string that your file system recognizes as a file name.
 extern const std::string PREF_SERVER_STAT_OF;
+// value: true | false
+extern const std::string PREF_REMOTE_TIME;
 
 /**
  * FTP related preferences

+ 81 - 0
test/CopyDiskAdaptorTest.cc

@@ -0,0 +1,81 @@
+#include "CopyDiskAdaptor.h"
+#include "FileEntry.h"
+#include "Exception.h"
+#include "a2io.h"
+#include "array_fun.h"
+#include "TestUtil.h"
+#include <string>
+#include <cerrno>
+#include <cstring>
+#include <cppunit/extensions/HelperMacros.h>
+
+namespace aria2 {
+
+class CopyDiskAdaptorTest:public CppUnit::TestFixture {
+
+  CPPUNIT_TEST_SUITE(CopyDiskAdaptorTest);
+  CPPUNIT_TEST(testUtime);
+  CPPUNIT_TEST_SUITE_END();
+
+public:
+  void setUp() {}
+
+  void testUtime();
+};
+
+
+CPPUNIT_TEST_SUITE_REGISTRATION( CopyDiskAdaptorTest );
+
+void CopyDiskAdaptorTest::testUtime()
+{
+  std::string storeDir = "/tmp";
+  std::string topDir = "aria2_CopyDiskAdaptorTest_testUtime";
+  std::string prefix = storeDir+"/"+topDir;
+  SharedHandle<FileEntry> entries[] = {
+    SharedHandle<FileEntry>(new FileEntry("requested", 10, 0)),
+    SharedHandle<FileEntry>(new FileEntry("notFound", 10, 10)),
+    SharedHandle<FileEntry>(new FileEntry("notRequested", 10, 20)),
+    SharedHandle<FileEntry>(new FileEntry("notExtracted", 10, 30)),
+    SharedHandle<FileEntry>(new FileEntry("anotherRequested", 10, 40)),
+  };
+
+  std::deque<SharedHandle<FileEntry> > fileEntries
+    (&entries[0], &entries[arrayLength(entries)]);
+  CopyDiskAdaptor adaptor;
+  adaptor.setStoreDir(storeDir);
+  adaptor.setTopDir(topDir);
+  adaptor.setFileEntries(fileEntries);
+
+  entries[0]->setExtracted(true);
+  entries[1]->setExtracted(true);
+  entries[2]->setExtracted(true);
+  entries[4]->setExtracted(true);
+  
+  entries[2]->setRequested(false);
+
+  createFile(prefix+"/"+entries[0]->getPath(), entries[0]->getLength());
+  File(prefix+"/"+entries[1]->getPath()).remove();
+  createFile(prefix+"/"+entries[2]->getPath(), entries[2]->getLength());
+  createFile(prefix+"/"+entries[3]->getPath(), entries[3]->getLength());
+  createFile(prefix+"/"+entries[4]->getPath(), entries[4]->getLength());
+
+  CPPUNIT_ASSERT_EQUAL((size_t)2, adaptor.utime(Time(1000), Time(2000)));
+  
+  CPPUNIT_ASSERT_EQUAL((time_t)2000,
+		       File(prefix+"/"+entries[0]->getPath())
+		       .getModifiedTime().getTime());
+
+  CPPUNIT_ASSERT_EQUAL((time_t)2000,
+		       File(prefix+"/"+entries[4]->getPath())
+		       .getModifiedTime().getTime());
+
+  CPPUNIT_ASSERT((time_t)2000 != File(prefix+"/"+entries[1]->getPath())
+		 .getModifiedTime().getTime());
+  CPPUNIT_ASSERT((time_t)2000 != File(prefix+"/"+entries[2]->getPath())
+		 .getModifiedTime().getTime());
+  CPPUNIT_ASSERT((time_t)2000 != File(prefix+"/"+entries[3]->getPath())
+		 .getModifiedTime().getTime());
+
+}
+
+} // namespace aria2

+ 19 - 0
test/FileTest.cc

@@ -1,4 +1,5 @@
 #include "File.h"
+#include "TestUtil.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
@@ -20,6 +21,7 @@ class FileTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testGetDirname);
   CPPUNIT_TEST(testGetBasename);
   CPPUNIT_TEST(testRenameTo);
+  CPPUNIT_TEST(testUtime);
   CPPUNIT_TEST_SUITE_END();
 private:
 
@@ -36,6 +38,7 @@ public:
   void testGetDirname();
   void testGetBasename();
   void testRenameTo();
+  void testUtime();
 };
 
 
@@ -208,4 +211,20 @@ void FileTest::testRenameTo()
   CPPUNIT_ASSERT(f.renameTo(fname));
 }
 
+void FileTest::testUtime()
+{
+  File f("/tmp/FileTest_testUTime");
+  createFile(f.getPath(), 0);
+  CPPUNIT_ASSERT(f.utime(Time(1000), Time(2000)));
+  
+  struct stat buf;
+  CPPUNIT_ASSERT(0 == stat(f.getPath().c_str(), &buf));
+  CPPUNIT_ASSERT_EQUAL((time_t)1000, buf.st_atime);
+  CPPUNIT_ASSERT_EQUAL((time_t)2000, f.getModifiedTime().getTime());
+
+  File notFound("/tmp/FileTest_testUTime_notFound");
+  notFound.remove();
+  CPPUNIT_ASSERT(!notFound.utime(Time(1000), Time(2000)));
+}
+
 } // namespace aria2

+ 2 - 1
test/Makefile.am

@@ -61,7 +61,8 @@ aria2c_SOURCES = AllTest.cc\
 	DirectDiskAdaptorTest.cc\
 	CookieTest.cc\
 	CookieStorageTest.cc\
-	TimeTest.cc
+	TimeTest.cc\
+	CopyDiskAdaptorTest.cc
 
 if HAVE_LIBZ
 aria2c_SOURCES += GZipDecoderTest.cc

+ 9 - 6
test/Makefile.in

@@ -194,8 +194,8 @@ am__aria2c_SOURCES_DIST = AllTest.cc TestUtil.cc TestUtil.h \
 	ServerStatURISelectorTest.cc InOrderURISelectorTest.cc \
 	ServerStatTest.cc NsCookieParserTest.cc \
 	DirectDiskAdaptorTest.cc CookieTest.cc CookieStorageTest.cc \
-	TimeTest.cc GZipDecoderTest.cc Sqlite3MozCookieParserTest.cc \
-	MessageDigestHelperTest.cc \
+	TimeTest.cc CopyDiskAdaptorTest.cc GZipDecoderTest.cc \
+	Sqlite3MozCookieParserTest.cc MessageDigestHelperTest.cc \
 	IteratableChunkChecksumValidatorTest.cc \
 	IteratableChecksumValidatorTest.cc BtAllowedFastMessageTest.cc \
 	BtBitfieldMessageTest.cc BtCancelMessageTest.cc \
@@ -366,8 +366,9 @@ am_aria2c_OBJECTS = AllTest.$(OBJEXT) TestUtil.$(OBJEXT) \
 	InOrderURISelectorTest.$(OBJEXT) ServerStatTest.$(OBJEXT) \
 	NsCookieParserTest.$(OBJEXT) DirectDiskAdaptorTest.$(OBJEXT) \
 	CookieTest.$(OBJEXT) CookieStorageTest.$(OBJEXT) \
-	TimeTest.$(OBJEXT) $(am__objects_1) $(am__objects_2) \
-	$(am__objects_3) $(am__objects_4) $(am__objects_5)
+	TimeTest.$(OBJEXT) CopyDiskAdaptorTest.$(OBJEXT) \
+	$(am__objects_1) $(am__objects_2) $(am__objects_3) \
+	$(am__objects_4) $(am__objects_5)
 aria2c_OBJECTS = $(am_aria2c_OBJECTS)
 am__DEPENDENCIES_1 =
 aria2c_DEPENDENCIES = ../src/libaria2c.a $(am__DEPENDENCIES_1)
@@ -589,8 +590,9 @@ aria2c_SOURCES = AllTest.cc TestUtil.cc TestUtil.h SocketCoreTest.cc \
 	ServerStatURISelectorTest.cc InOrderURISelectorTest.cc \
 	ServerStatTest.cc NsCookieParserTest.cc \
 	DirectDiskAdaptorTest.cc CookieTest.cc CookieStorageTest.cc \
-	TimeTest.cc $(am__append_1) $(am__append_2) $(am__append_3) \
-	$(am__append_4) $(am__append_5)
+	TimeTest.cc CopyDiskAdaptorTest.cc $(am__append_1) \
+	$(am__append_2) $(am__append_3) $(am__append_4) \
+	$(am__append_5)
 
 #aria2c_CXXFLAGS = ${CPPUNIT_CFLAGS} -I../src -I../lib -Wall -D_FILE_OFFSET_BITS=64
 #aria2c_LDFLAGS = ${CPPUNIT_LIBS}
@@ -712,6 +714,7 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieParserTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieStorageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CookieTest.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/CopyDiskAdaptorTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHKeyExchangeTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHTAnnouncePeerMessageTest.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DHTAnnouncePeerReplyMessageTest.Po@am__quote@

+ 43 - 2
test/MultiDiskAdaptorTest.cc

@@ -18,6 +18,7 @@ class MultiDiskAdaptorTest:public CppUnit::TestFixture {
   CPPUNIT_TEST(testReadData);
   CPPUNIT_TEST(testCutTrailingGarbage);
   CPPUNIT_TEST(testSize);
+  CPPUNIT_TEST(testUtime);
   CPPUNIT_TEST_SUITE_END();
 private:
   SharedHandle<MultiDiskAdaptor> adaptor;
@@ -33,6 +34,7 @@ public:
   void testReadData();
   void testCutTrailingGarbage();
   void testSize();
+  void testUtime();
 };
 
 
@@ -181,8 +183,7 @@ void MultiDiskAdaptorTest::testSize()
     SharedHandle<FileEntry>(new FileEntry(prefix+"2", 1, 1))
   };
   for(size_t i = 0; i < arrayLength(entries); ++i) {
-    createFile(topDirPath+"/"+entries[i]->getPath(),
-	       entries[i]->getLength());
+    createFile(topDirPath+"/"+entries[i]->getPath(), entries[i]->getLength());
   }
   std::deque<SharedHandle<FileEntry> > fileEntries
     (&entries[0], &entries[arrayLength(entries)]);
@@ -199,4 +200,44 @@ void MultiDiskAdaptorTest::testSize()
   CPPUNIT_ASSERT_EQUAL((uint64_t)2, adaptor.size());
 }
 
+void MultiDiskAdaptorTest::testUtime()
+{
+  std::string storeDir = "/tmp";
+  std::string topDir = "aria2_MultiDiskAdaptorTest_testUtime";
+  std::string prefix = storeDir+"/"+topDir;
+  SharedHandle<FileEntry> entries[] = {
+    SharedHandle<FileEntry>(new FileEntry("requested", 0, 0)),
+    SharedHandle<FileEntry>(new FileEntry("notFound", 0, 0)),
+    SharedHandle<FileEntry>(new FileEntry("notRequested", 0, 0)),
+    SharedHandle<FileEntry>(new FileEntry("anotherRequested", 0, 0)),
+  };
+
+  createFile(prefix+"/"+entries[0]->getPath(), entries[0]->getLength());
+  File(prefix+"/"+entries[1]->getPath()).remove();
+  createFile(prefix+"/"+entries[2]->getPath(), entries[2]->getLength());
+  createFile(prefix+"/"+entries[3]->getPath(), entries[3]->getLength());
+
+  entries[2]->setRequested(false);
+
+  std::deque<SharedHandle<FileEntry> > fileEntries
+    (&entries[0], &entries[arrayLength(entries)]);
+  MultiDiskAdaptor adaptor;
+  adaptor.setStoreDir(storeDir);
+  adaptor.setTopDir(topDir);
+  adaptor.setFileEntries(fileEntries);
+
+  CPPUNIT_ASSERT_EQUAL((size_t)2, adaptor.utime(Time(1000), Time(2000)));
+  
+  CPPUNIT_ASSERT_EQUAL((time_t)2000,
+		       File(prefix+"/"+entries[0]->getPath())
+		       .getModifiedTime().getTime());
+
+  CPPUNIT_ASSERT_EQUAL((time_t)2000,
+		       File(prefix+"/"+entries[3]->getPath())
+		       .getModifiedTime().getTime());
+
+  CPPUNIT_ASSERT((time_t)2000 != File(prefix+"/"+entries[2]->getPath())
+		 .getModifiedTime().getTime());
+}
+
 } // namespace aria2

+ 15 - 1
test/TestUtil.cc

@@ -1,15 +1,29 @@
 #include "TestUtil.h"
 #include "a2io.h"
+#include "File.h"
+#include "StringFormat.h"
+#include "FatalException.h"
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <cerrno>
+#include <cstring>
 
 namespace aria2 {
 
 void createFile(const std::string& path, size_t length)
 {
+  File(File(path).getDirname()).mkdirs();
   int fd = creat(path.c_str(), OPEN_MODE);
-  ftruncate(fd, length);
+  if(fd == -1) {
+    throw FatalException(StringFormat("Could not create file=%s. cause:%s",
+				      path.c_str(), strerror(errno)).str());
+  }
+  if(-1 == ftruncate(fd, length)) {
+    throw FatalException(StringFormat("ftruncate failed. cause:%s",
+				      strerror(errno)).str());
+  }
+  close(fd);
 }
 
 };