File indexing completed on 2024-11-24 04:31:16
0001 /* 0002 SPDX-FileCopyrightText: 2005 Joris Guisson <joris.guisson@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 #include "singlefilecache.h" 0007 #include <QTextStream> 0008 #include <kio/copyjob.h> 0009 #include <kio/jobuidelegate.h> 0010 #include <klocalizedstring.h> 0011 #include <qfileinfo.h> 0012 #include <qstringlist.h> 0013 #include <util/error.h> 0014 #include <util/fileops.h> 0015 #include <util/functions.h> 0016 #include <util/log.h> 0017 #ifdef Q_WS_WIN 0018 #include <util/win32.h> 0019 #endif 0020 #include "cachefile.h" 0021 #include "chunk.h" 0022 #include "deletedatafilesjob.h" 0023 #include "movedatafilesjob.h" 0024 #include "piecedata.h" 0025 #include "preallocationthread.h" 0026 #include <torrent/torrent.h> 0027 0028 namespace bt 0029 { 0030 SingleFileCache::SingleFileCache(Torrent &tor, const QString &tmpdir, const QString &datadir) 0031 : Cache(tor, tmpdir, datadir) 0032 , fd(nullptr) 0033 { 0034 cache_file = tmpdir + "cache"; 0035 QFileInfo fi(cache_file); 0036 if (fi.isSymLink()) // old style symlink 0037 output_file = fi.symLinkTarget(); 0038 else 0039 output_file = datadir + tor.getNameSuggestion(); 0040 } 0041 0042 SingleFileCache::~SingleFileCache() 0043 { 0044 cleanupPieceCache(); 0045 } 0046 0047 void SingleFileCache::loadFileMap() 0048 { 0049 QString file_map = tmpdir + "file_map"; 0050 if (!bt::Exists(file_map)) { 0051 saveFileMap(); 0052 return; 0053 } 0054 0055 QFile fptr(file_map); 0056 if (!fptr.open(QIODevice::ReadOnly)) 0057 throw Error(i18n("Failed to open %1: %2", file_map, fptr.errorString())); 0058 0059 output_file = QString::fromLocal8Bit(fptr.readLine().trimmed()); 0060 } 0061 0062 void SingleFileCache::saveFileMap() 0063 { 0064 QString file_map = tmpdir + "file_map"; 0065 QFile fptr(file_map); 0066 if (!fptr.open(QIODevice::WriteOnly)) 0067 throw Error(i18n("Failed to create %1: %2", file_map, fptr.errorString())); 0068 0069 QTextStream out(&fptr); 0070 out << output_file << Qt::endl; 0071 } 0072 0073 void SingleFileCache::changeTmpDir(const QString &ndir) 0074 { 0075 Cache::changeTmpDir(ndir); 0076 cache_file = tmpdir + "cache"; 0077 } 0078 0079 void SingleFileCache::changeOutputPath(const QString &outputpath) 0080 { 0081 close(); 0082 output_file = outputpath; 0083 datadir = output_file.left(output_file.lastIndexOf(bt::DirSeparator())); 0084 saveFileMap(); 0085 } 0086 0087 Job *SingleFileCache::moveDataFiles(const QString &ndir) 0088 { 0089 QString dst = ndir; 0090 if (!dst.endsWith(bt::DirSeparator())) 0091 dst += bt::DirSeparator(); 0092 0093 dst += output_file.mid(output_file.lastIndexOf(bt::DirSeparator()) + 1); 0094 if (output_file == dst) 0095 return nullptr; 0096 0097 move_data_files_dst = dst; 0098 MoveDataFilesJob *job = new MoveDataFilesJob(); 0099 job->addMove(output_file, dst); 0100 return job; 0101 } 0102 0103 void SingleFileCache::moveDataFilesFinished(Job *job) 0104 { 0105 if (job->error() == KIO::ERR_USER_CANCELED) { 0106 if (bt::Exists(move_data_files_dst)) 0107 bt::Delete(move_data_files_dst, true); 0108 } 0109 move_data_files_dst = QString(); 0110 } 0111 0112 PieceData::Ptr SingleFileCache::createPiece(Chunk *c, Uint64 off, Uint32 length, bool read_only) 0113 { 0114 if (!fd) 0115 open(); 0116 0117 Uint64 piece_off = c->getIndex() * tor.getChunkSize() + off; 0118 Uint8 *buf = nullptr; 0119 if (mmap_failures >= 3) { 0120 buf = new Uint8[length]; 0121 PieceData::Ptr cp(new PieceData(c, off, length, buf, CacheFile::Ptr(), read_only)); 0122 insertPiece(c, cp); 0123 return cp; 0124 } else { 0125 PieceData::Ptr cp(new PieceData(c, off, length, nullptr, fd, read_only)); 0126 buf = (Uint8 *)fd->map(cp.data(), piece_off, length, read_only ? CacheFile::READ : CacheFile::RW); 0127 if (buf) { 0128 cp->setData(buf); 0129 } else { 0130 if (mmap_failures < 3) 0131 mmap_failures++; 0132 0133 buf = new Uint8[length]; 0134 cp = PieceData::Ptr(new PieceData(c, off, length, buf, CacheFile::Ptr(), read_only)); 0135 } 0136 insertPiece(c, cp); 0137 return cp; 0138 } 0139 } 0140 0141 PieceData::Ptr SingleFileCache::loadPiece(Chunk *c, Uint32 off, Uint32 length) 0142 { 0143 PieceData::Ptr cp = findPiece(c, off, length, true); 0144 if (cp) 0145 return cp; 0146 0147 cp = createPiece(c, off, length, true); 0148 if (cp && !cp->mapped()) { 0149 // read data from file if piece isn't mapped 0150 Uint64 piece_off = c->getIndex() * tor.getChunkSize() + off; 0151 fd->read(cp->data(), length, piece_off); 0152 } 0153 0154 return cp; 0155 } 0156 0157 PieceData::Ptr SingleFileCache::preparePiece(Chunk *c, Uint32 off, Uint32 length) 0158 { 0159 PieceData::Ptr cp = findPiece(c, off, length, false); 0160 if (cp) 0161 return cp; 0162 0163 return createPiece(c, off, length, false); 0164 } 0165 0166 void SingleFileCache::savePiece(PieceData::Ptr piece) 0167 { 0168 if (!fd) 0169 open(); 0170 0171 // mapped pieces will be unmapped when they are destroyed, buffered ones need to be written 0172 if (!piece->mapped()) { 0173 Uint64 off = piece->parentChunk()->getIndex() * tor.getChunkSize() + piece->offset(); 0174 if (piece->ok()) 0175 fd->write(piece->data(), piece->length(), off); 0176 } 0177 } 0178 0179 void SingleFileCache::create() 0180 { 0181 // check for a to long path name 0182 if (FileNameToLong(output_file)) 0183 output_file = ShortenFileName(output_file); 0184 0185 if (!bt::Exists(output_file)) { 0186 MakeFilePath(output_file); 0187 bt::Touch(output_file); 0188 } else 0189 preexisting_files = true; 0190 0191 QSet<QString> mps; 0192 mps.insert(MountPoint(output_file)); 0193 saveMountPoints(mps); 0194 saveFileMap(); 0195 } 0196 0197 bool SingleFileCache::getMountPoints(QSet<QString> &mps) 0198 { 0199 QString mp = MountPoint(output_file); 0200 if (mp.isEmpty()) 0201 return false; 0202 0203 mps.insert(mp); 0204 return true; 0205 } 0206 0207 void SingleFileCache::close() 0208 { 0209 clearPieceCache(); 0210 if (fd && piece_cache.isEmpty()) { 0211 fd.clear(); 0212 } 0213 } 0214 0215 void SingleFileCache::open() 0216 { 0217 if (fd) 0218 return; 0219 0220 CacheFile::Ptr tmp(new CacheFile()); 0221 tmp->open(output_file, tor.getTotalSize()); 0222 fd = tmp; 0223 } 0224 0225 void SingleFileCache::preparePreallocation(PreallocationThread *prealloc) 0226 { 0227 if (!fd) 0228 open(); 0229 0230 prealloc->add(fd); 0231 } 0232 0233 bool SingleFileCache::hasMissingFiles(QStringList &sl) 0234 { 0235 if (!bt::Exists(output_file)) { 0236 sl.append(output_file); 0237 return true; 0238 } 0239 return false; 0240 } 0241 0242 Job *SingleFileCache::deleteDataFiles() 0243 { 0244 DeleteDataFilesJob *job = new DeleteDataFilesJob(""); 0245 job->addFile(output_file); 0246 return job; 0247 } 0248 0249 Uint64 SingleFileCache::diskUsage() 0250 { 0251 if (!fd) 0252 open(); 0253 0254 return fd->diskUsage(); 0255 } 0256 }