/** ** File ......... FileManager.cpp ** Published .... 2005-06-14 ** Author ....... grymse@alhem.net **/ /* Copyright (C) 2005 Anders Hedstrom This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #ifdef _WIN32 #pragma warning(disable:4786) #endif #include #include #include #ifndef _WIN32 #include #endif #include #include "PeerHandler.h" #include "Session.h" #include "FileManager.h" #define DEB(x) x FileManager::FileManager(PeerHandler& h,const std::string& hash) :m_hash(hash) ,m_handler(h) ,m_single_file(NULL) { DEB( printf("FileManager()\n");) Session *sess = h.GetSession(hash); if (sess) { m_basename = h.GetTorrentDirectory() + "/" + sess -> GetInfoHash() + "/" + sess -> GetName(); GetFiles(m_files); m_length = sess -> GetLength(); DEB( printf("OpenFiles()\n");) OpenFiles(); } } FileManager::~FileManager() { DEB( printf("~FileManager()\n");) if (m_single_file) { fclose(m_single_file); } for (fil_v::iterator it = m_files.begin(); it != m_files.end(); it++) { FIL *p = *it; if (p -> fil) { fclose(p -> fil); } delete p; } } void FileManager::OpenFiles() { if (m_length) // single file { std::string full = m_basename; //DEB(printf("single file: %s\n", full.c_str());) Handler().mkpath( full ); m_single_file = fopen(full.c_str(),"r+b"); if (!m_single_file) { m_single_file = fopen(full.c_str(),"w+b"); } size_t size = GetFilesize(full); if (size > m_length) { DEB( printf("Single file already too large - aborting\n");) } return; } // multi file for (fil_v::iterator it = m_files.begin(); it != m_files.end(); it++) { FIL *p = *it; std::string full = p -> path; //DEB(printf("multifile: %s\n", full.c_str());) Handler().mkpath( full ); p -> fil = fopen(full.c_str(),"r+b"); if (!p -> fil) { p -> fil = fopen(full.c_str(),"w+b"); } size_t size = GetFilesize(full); if (size > p -> length) { DEB( printf("File '%s' too large - aborting\n",full.c_str());) } } } size_t FileManager::ReadPiece(size_t index,unsigned char *buf,size_t length) { Session *sess = Handler().GetSession(m_hash); if (!sess) return 0; size_t piece_length = sess -> GetPieceLength(); if (m_length) { long offset = index * piece_length; if (fseek(m_single_file,offset,SEEK_SET)) { DEB( perror("fseek() failed");) } size_t n = fread(buf,1,length,m_single_file); buf[0]++; return n; } int64_t start = index * piece_length; size_t bufset = 0; // offset in buf size_t left = piece_length; int64_t ptr = 0; size_t read_length = 0; //DEB(printf("ReadPiece(%d, ptr = %lld)\n", index, ptr);) for (fil_v::iterator it = m_files.begin(); it != m_files.end() && left; it++) { FIL *p = *it; // start within this file? if (start < ptr + p -> length) { long offset = start - ptr; size_t len = p -> length - offset; if (len > left) { len = left; } if (fseek(p -> fil,offset,SEEK_SET)) { DEB( perror("fseek() failed");) } read_length += fread(buf + bufset,1,len,p -> fil); // update start += len; bufset += len; left -= len; if (!left) return read_length; } ptr += p -> length; } buf[0]++; return read_length; } void FileManager::Write(size_t index,unsigned char *buf,size_t length,size_t begin) { Session *sess = Handler().GetSession(m_hash); if (!sess) return; size_t piece_length = sess -> GetPieceLength(); if (m_length) { long offset = index * piece_length + begin; if (fseek(m_single_file,offset,SEEK_SET)) { DEB( perror("fseek() failed");) } long pos = ftell(m_single_file); if (pos < offset) { long diff = offset - pos; char *tmp = new char[diff]; memset(tmp,0,diff); fwrite(tmp,1,diff,m_single_file); delete tmp; } fwrite(buf,1,length,m_single_file); fflush(m_single_file); return; } int64_t start = index * piece_length + begin; size_t bufset = 0; // offset in buf size_t left = length; int64_t ptr = 0; for (fil_v::iterator it = m_files.begin(); it != m_files.end() && left; it++) { FIL *p = *it; // start within this file? if (start < ptr + p -> length) { long offset = start - ptr; size_t len = p -> length - offset; // length of file remaining from start of this write if (len > left) { len = left; } //DEB(printf("writing@%08x length %04x\n",offset,len);) if (fseek(p -> fil,offset,SEEK_SET)) { DEB( perror("fseek() failed");) } long pos = ftell(p -> fil); if (pos < offset) { long diff = offset - pos; char *tmp = new char[diff]; memset(tmp,0,diff); fwrite(tmp,1,diff,p -> fil); delete tmp; DEB(printf(" adding %ld bytes\n",diff);) } if (fwrite(buf + bufset,1,len,p -> fil) != len) { DEB( printf(" * short write!!!!!!\n");) } fflush(p -> fil); // update start += len; bufset += len; left -= len; } ptr += p -> length; } } size_t FileManager::GetFilesize(const std::string& path) { struct stat st; stat(path.c_str(),&st); return st.st_size; } uint64_t FileManager::GetTotalLength() { uint64_t l = 0; for (fil_v::iterator it = m_files.begin(); it != m_files.end(); it++) { FIL *p = *it; l += p -> length; } return l; } void FileManager::GetFiles(FileManager::fil_v& files) { Session *sess = Handler().GetSession(m_hash); if (!sess) return; file_v& ref = sess -> GetFiles(); for (file_v::iterator it = ref.begin(); it != ref.end(); it++) { file_t *f = *it; // name, offset, length std::string full = Handler().GetTorrentDirectory() + "/" + sess -> GetInfoHash() + "/" + f -> name; FIL *p2 = new FIL(f -> length, full); files.push_back(p2); } } bool FileManager::Verify(size_t index, size_t length) { Session *sess = Handler().GetSession(m_hash); if (!sess) { DEB(printf("Verify: no session\n");) return false; } Piece *ptr = sess -> GetIncomplete(index); if (!ptr) { DEB(printf("Verify: no piece ptr\n");) return false; } unsigned char *p = new unsigned char[length]; unsigned char hash[20]; size_t read_length; read_length = ReadPiece(index, p, length ); if (read_length != length) { //DEB( printf(" *** Verify(): only read %d bytes out of %d\n",read_length,length);) delete[] p; return false; } SHA1( p, read_length, hash ); delete[] p; if (CompareHash(hash,(unsigned char *)sess -> GetPieces().substr(index * 20,20).c_str())) { DEB( printf("Verify OK!\n");) return true; } //DEB( printf("Verify failed\n");) return false; } bool FileManager::CompareHash(unsigned char *h1,unsigned char *h2) { for (size_t i = 0; i < 20; i++) if (h1[i] != h2[i]) return false; return true; }