Forráskód Böngészése

Use Win32 API for file I/O in AbstractDiskWriter

Now file has been opened by aria2 is now readable by other processes.
Tatsuhiro Tsujikawa 13 éve
szülő
commit
b95f15b462
2 módosított fájl, 140 hozzáadás és 41 törlés
  1. 135 41
      src/AbstractDiskWriter.cc
  2. 5 0
      src/AbstractDiskWriter.h

+ 135 - 41
src/AbstractDiskWriter.cc

@@ -53,11 +53,17 @@
 #include "error_code.h"
 #include "LogFactory.h"
 
+#ifdef __MINGW32__
+#  define A2_BAD_FILE_DESCRIPTOR INVALID_HANDLE_VALUE
+#else // !__MINGW32__
+#  define A2_BAD_FILE_DESCRIPTOR -1
+#endif // !__MINGW32__
+
 namespace aria2 {
 
 AbstractDiskWriter::AbstractDiskWriter(const std::string& filename)
   : filename_(filename),
-    fd_(-1),
+    fd_(A2_BAD_FILE_DESCRIPTOR),
     readOnly_(false),
     enableMmap_(false),
     mapaddr_(0),
@@ -69,12 +75,51 @@ AbstractDiskWriter::~AbstractDiskWriter()
   closeFile();
 }
 
+namespace {
+int fileError()
+{
+#ifdef __MINGW32__
+  return GetLastError();
+#else // !__MINGW32__
+  return errno;
+#endif // !__MINGW32__
+}
+} // namespace
+
+namespace {
+std::string fileStrerror(int errNum)
+{
+#ifdef __MINGW32__
+  static char buf[256];
+  if(FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+                   0,
+                   errNum,
+                   // Default language
+                   MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+                   (LPTSTR) &buf,
+                   sizeof(buf),
+                   0) == 0) {
+    snprintf(buf, sizeof(buf), "File IO error %x", errNum);
+  }
+  return buf;
+#else // !__MINGW32__
+  return util::safeStrerror(errNum);
+#endif // !__MINGW32__
+}
+} // namespace
+
 void AbstractDiskWriter::openFile(int64_t totalLength)
 {
   try {
     openExistingFile(totalLength);
   } catch(RecoverableException& e) {
-    if(e.getErrNum() == ENOENT) {
+    if(
+#ifdef __MINGW32__
+       e.getErrNum() == ERROR_FILE_NOT_FOUND
+#else // !__MINGW32__
+       e.getErrNum() == ENOENT
+#endif // !__MINGW32__
+       ) {
       initAndOpenFile(totalLength);
     } else {
       throw;
@@ -98,24 +143,61 @@ void AbstractDiskWriter::closeFile()
     maplen_ = 0;
   }
 #endif // HAVE_MMAP
-  if(fd_ != -1) {
+  if(fd_ != A2_BAD_FILE_DESCRIPTOR) {
+#ifdef __MINGW32__
+    CloseHandle(fd_);
+#else // !__MINGW32__
     close(fd_);
-    fd_ = -1;
+#endif // !__MINGW32__
+    fd_ = A2_BAD_FILE_DESCRIPTOR;
   }
 }
 
 namespace {
+#ifdef __MINGW32__
+HANDLE openFileWithFlags(const std::string& filename, int flags,
+                         error_code::Value errCode)
+{
+  HANDLE hn;
+  DWORD desiredAccess = 0, sharedMode = FILE_SHARE_READ, creationDisp = 0;
+  if(flags & O_RDONLY) {
+    desiredAccess |= GENERIC_READ;
+  } else if(flags & O_RDWR) {
+    desiredAccess |= GENERIC_READ | GENERIC_WRITE;
+  }
+  if(flags & O_CREAT) {
+    if(flags & O_TRUNC) {
+      creationDisp |= CREATE_ALWAYS;
+    } else {
+      creationDisp |= CREATE_NEW;
+    }
+  } else {
+    creationDisp |= OPEN_EXISTING;
+  }
+  hn = CreateFileW(utf8ToWChar(filename).c_str(),
+                   desiredAccess, sharedMode, 0, creationDisp,
+                   FILE_ATTRIBUTE_NORMAL, 0);
+  if(hn == INVALID_HANDLE_VALUE) {
+    int errNum = GetLastError();
+    throw DL_ABORT_EX3(errNum, fmt(EX_FILE_OPEN,
+                                   filename.c_str(),
+                                   fileStrerror(errNum).c_str()),
+                       errCode);
+  }
+  return hn;
+}
+#else // !__MINGW32__
 int openFileWithFlags(const std::string& filename, int flags,
                       error_code::Value errCode)
 {
   int fd;
-  while((fd = a2open(utf8ToWChar(filename).c_str(), flags, OPEN_MODE)) == -1
+  while((fd = a2open(filename.c_str(), flags, OPEN_MODE)) == -1
         && errno == EINTR);
   if(fd < 0) {
     int errNum = errno;
     throw DL_ABORT_EX3(errNum, fmt(EX_FILE_OPEN,
                                    filename.c_str(),
-                                   util::safeStrerror(errNum).c_str()),
+                                   fileStrerror(errNum).c_str()),
                        errCode);
   }
 #if defined(__APPLE__) && defined(__MACH__)
@@ -123,6 +205,7 @@ int openFileWithFlags(const std::string& filename, int flags,
 #endif // __APPLE__ && __MACH__
   return fd;
 }
+#endif // !__MINGW32__
 } // namespace
 
 void AbstractDiskWriter::openExistingFile(int64_t totalLength)
@@ -154,12 +237,19 @@ ssize_t AbstractDiskWriter::writeDataInternal(const unsigned char* data,
     ssize_t writtenLength = 0;
     seek(offset);
     while((size_t)writtenLength < len) {
+#ifdef __MINGW32__
+      DWORD ret;
+      if(!WriteFile(fd_, data, len, &ret, 0)) {
+        return -1;
+      }
+#else // !__MINGW32__
       ssize_t ret = 0;
       while((ret = write(fd_, data+writtenLength, len-writtenLength)) == -1 &&
             errno == EINTR);
       if(ret == -1) {
         return -1;
       }
+#endif // !__MINGW32__
       writtenLength += ret;
     }
     return writtenLength;
@@ -179,20 +269,34 @@ ssize_t AbstractDiskWriter::readDataInternal(unsigned char* data, size_t len,
     memcpy(data, mapaddr_ + offset, readlen);
     return readlen;
   } else {
-    ssize_t ret = 0;
     seek(offset);
+#ifdef __MINGW32__
+    DWORD ret;
+    if(!ReadFile(fd_, data, len, &ret, 0)) {
+      ret = -1;
+    }
+#else // !__MINGW32__
+    ssize_t ret = 0;
     while((ret = read(fd_, data, len)) == -1 && errno == EINTR);
+#endif // !__MINGW32__
     return ret;
   }
 }
 
 void AbstractDiskWriter::seek(int64_t offset)
 {
+#ifdef __MINGW32__
+  LARGE_INTEGER fileLength;
+  fileLength.QuadPart = offset;
+  if(SetFilePointerEx(fd_, fileLength, 0, FILE_BEGIN) == 0) {
+    int errNum = GetLastError();
+#else // !__MINGW32__
   if(a2lseek(fd_, offset, SEEK_SET) == (off_t)-1) {
     int errNum = errno;
+#endif // !__MINGW32__
     throw DL_ABORT_EX2(fmt(EX_FILE_SEEK,
                            filename_.c_str(),
-                           util::safeStrerror(errNum).c_str()),
+                           fileStrerror(errNum).c_str()),
                        error_code::FILE_IO_ERROR);
   }
 }
@@ -234,22 +338,25 @@ void AbstractDiskWriter::writeData(const unsigned char* data, size_t len, int64_
 {
   ensureMmapWrite(len, offset);
   if(writeDataInternal(data, len, offset) < 0) {
-    int errNum = errno;
-    // If errno is ENOSPC(not enough space in device), throw
+    int errNum = fileError();
+    // If errNum is ENOSPC(not enough space in device) (or
+    // ERROR_HANDLE_DISK_FULL on MinGW), throw
     // DownloadFailureException and abort download instantly.
-    if(errNum == ENOSPC) {
+    if(
+#ifdef __MINGW32__
+       errNum == ERROR_HANDLE_DISK_FULL
+#else // !__MINGW32__
+       errNum == ENOSPC
+#endif // !__MINGW32__
+       ) {
       throw DOWNLOAD_FAILURE_EXCEPTION3
         (errNum,
-         fmt(EX_FILE_WRITE,
-             filename_.c_str(),
-             util::safeStrerror(errNum).c_str()),
+         fmt(EX_FILE_WRITE, filename_.c_str(), fileStrerror(errNum).c_str()),
          error_code::NOT_ENOUGH_DISK_SPACE);
     } else {
       throw DL_ABORT_EX3
         (errNum,
-         fmt(EX_FILE_WRITE,
-             filename_.c_str(),
-             util::safeStrerror(errNum).c_str()),
+         fmt(EX_FILE_WRITE, filename_.c_str(), fileStrerror(errNum).c_str()),
          error_code::FILE_IO_ERROR);
     }
   }
@@ -259,12 +366,10 @@ ssize_t AbstractDiskWriter::readData(unsigned char* data, size_t len, int64_t of
 {
   ssize_t ret;
   if((ret = readDataInternal(data, len, offset)) < 0) {
-    int errNum = errno;
+    int errNum = fileError();
     throw DL_ABORT_EX3
       (errNum,
-       fmt(EX_FILE_READ,
-           filename_.c_str(),
-           util::safeStrerror(errNum).c_str()),
+       fmt(EX_FILE_READ, filename_.c_str(), fileStrerror(errNum).c_str()),
        error_code::FILE_IO_ERROR);
   }
   return ret;
@@ -272,15 +377,14 @@ ssize_t AbstractDiskWriter::readData(unsigned char* data, size_t len, int64_t of
 
 void AbstractDiskWriter::truncate(int64_t length)
 {
-  if(fd_ == -1) {
-    throw DL_ABORT_EX("File not opened.");
+  if(fd_ == A2_BAD_FILE_DESCRIPTOR) {
+    throw DL_ABORT_EX("File not yet opened.");
   }
 #ifdef __MINGW32__
-  // Since mingw32's ftruncate cannot handle over 2GB files, we use SetEndOfFile
-  // instead.
-  HANDLE handle = LongToHandle(_get_osfhandle(fd_));
+  // Since mingw32's ftruncate cannot handle over 2GB files, we use
+  // SetEndOfFile instead.
   seek(length);
-  if(SetEndOfFile(handle) == 0) {
+  if(SetEndOfFile(fd_) == 0) {
     throw DL_ABORT_EX2(fmt("SetEndOfFile failed. cause: %lx",
                            GetLastError()),
                        error_code::FILE_IO_ERROR);
@@ -298,23 +402,13 @@ void AbstractDiskWriter::truncate(int64_t length)
 
 void AbstractDiskWriter::allocate(int64_t offset, int64_t length)
 {
-#ifdef  HAVE_SOME_FALLOCATE
-  if(fd_ == -1) {
+  if(fd_ == A2_BAD_FILE_DESCRIPTOR) {
     throw DL_ABORT_EX("File not yet opened.");
   }
+#ifdef  HAVE_SOME_FALLOCATE
 # ifdef __MINGW32__
-  LARGE_INTEGER fileLength;
-  fileLength.QuadPart = offset+length;
-  HANDLE handle = LongToHandle(_get_osfhandle(fd_));
-  int r;
-  r = SetFilePointerEx(handle, fileLength, 0, FILE_BEGIN);
-  if(r == 0) {
-    throw DL_ABORT_EX2(fmt("SetFilePointerEx failed. cause: %lx",
-                           GetLastError()),
-                       error_code::FILE_IO_ERROR);
-  }
-  r = SetEndOfFile(handle);
-  if(r == 0) {
+  seek(offset+length);
+  if(SetEndOfFile(fd_) == 0) {
     throw DL_ABORT_EX2(fmt("SetEndOfFile failed. cause: %lx",
                            GetLastError()),
                        error_code::FILE_IO_ERROR);

+ 5 - 0
src/AbstractDiskWriter.h

@@ -43,7 +43,12 @@ namespace aria2 {
 class AbstractDiskWriter : public DiskWriter {
 private:
   std::string filename_;
+
+#ifdef __MINGW32__
+  HANDLE fd_;
+#else // !__MINGW32__
   int fd_;
+#endif // !__MINGW32__
 
   bool readOnly_;