/* */ #include "AbstractDiskWriter.h" #include "File.h" #include "Util.h" #include "message.h" #include "LogFactory.h" #include "Logger.h" #include "DlAbortEx.h" #include "a2io.h" #include "StringFormat.h" #include "DownloadFailureException.h" #include #include #include namespace aria2 { AbstractDiskWriter::AbstractDiskWriter(): fd(-1), logger(LogFactory::getInstance()) {} AbstractDiskWriter::~AbstractDiskWriter() { closeFile(); } void AbstractDiskWriter::openFile(const std::string& filename, uint64_t totalLength) { File f(filename); if(f.exists()) { openExistingFile(filename, totalLength); } else { initAndOpenFile(filename, totalLength); } } void AbstractDiskWriter::closeFile() { if(fd >= 0) { close(fd); fd = -1; } } void AbstractDiskWriter::openExistingFile(const std::string& filename, uint64_t totalLength) { this->filename = filename; File f(filename); if(!f.exists()) { throw DlAbortEx (StringFormat(EX_FILE_OPEN, filename.c_str(), MSG_FILE_NOT_FOUND).str()); } if((fd = open(filename.c_str(), O_RDWR|O_BINARY, OPEN_MODE)) < 0) { throw DlAbortEx (StringFormat(EX_FILE_OPEN, filename.c_str(), strerror(errno)).str()); } } void AbstractDiskWriter::createFile(const std::string& filename, int addFlags) { this->filename = filename; assert(filename.size()); Util::mkdirs(File(filename).getDirname()); if((fd = open(filename.c_str(), O_CREAT|O_RDWR|O_TRUNC|O_BINARY|addFlags, OPEN_MODE)) < 0) { throw DlAbortEx(StringFormat(EX_FILE_OPEN, filename.c_str(), strerror(errno)).str()); } } ssize_t AbstractDiskWriter::writeDataInternal(const unsigned char* data, size_t len) { ssize_t writtenLength = 0; while((size_t)writtenLength < len) { ssize_t ret = 0; while((ret = write(fd, data+writtenLength, len-writtenLength)) == -1 && errno == EINTR); if(ret == -1) { return -1; } writtenLength += ret; } return writtenLength; } ssize_t AbstractDiskWriter::readDataInternal(unsigned char* data, size_t len) { ssize_t ret = 0; while((ret = read(fd, data, len)) == -1 && errno == EINTR); return ret; } void AbstractDiskWriter::seek(off_t offset) { if(offset != lseek(fd, offset, SEEK_SET)) { throw DlAbortEx (StringFormat(EX_FILE_SEEK, filename.c_str(), strerror(errno)).str()); } } void AbstractDiskWriter::writeData(const unsigned char* data, size_t len, off_t offset) { seek(offset); if(writeDataInternal(data, len) < 0) { // If errno is ENOSPC(not enough space in device), throw // DownloadFailureException and abort download instantly. if(errno == ENOSPC) { throw DownloadFailureException (StringFormat(EX_FILE_WRITE, filename.c_str(), strerror(errno)).str()); } throw DlAbortEx(StringFormat(EX_FILE_WRITE, filename.c_str(), strerror(errno)).str()); } } ssize_t AbstractDiskWriter::readData(unsigned char* data, size_t len, off_t offset) { ssize_t ret; seek(offset); if((ret = readDataInternal(data, len)) < 0) { throw DlAbortEx(StringFormat(EX_FILE_READ, filename.c_str(), strerror(errno)).str()); } return ret; } void AbstractDiskWriter::truncate(uint64_t length) { if(fd == -1) { throw DlAbortEx("File not opened."); } ftruncate(fd, length); } // TODO the file descriptor fd must be opened before calling this function. uint64_t AbstractDiskWriter::size() const { if(fd == -1) { throw DlAbortEx("File not opened."); } a2_struct_stat fileStat; if(fstat(fd, &fileStat) < 0) { return 0; } return fileStat.st_size; } void AbstractDiskWriter::enableDirectIO() { #ifdef ENABLE_DIRECT_IO if(_directIOAllowed) { int flg; while((flg = fcntl(fd, F_GETFL)) == -1 && errno == EINTR); while(fcntl(fd, F_SETFL, flg|O_DIRECT) == -1 && errno == EINTR); } #endif // ENABLE_DIRECT_IO } void AbstractDiskWriter::disableDirectIO() { #ifdef ENABLE_DIRECT_IO int flg; while((flg = fcntl(fd, F_GETFL)) == -1 && errno == EINTR); while(fcntl(fd, F_SETFL, flg&(~O_DIRECT)) == -1 && errno == EINTR); #endif // ENABLE_DIRECT_IO } } // namespace aria2