blob: 943330ead19493fa78249b3ce7ebd5853583e377 [file] [log] [blame]
spirosmastorakis386cc542016-05-06 14:37:26 -07001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/**
3* Copyright (c) 2016 Regents of the University of California.
4*
5* This file is part of the nTorrent codebase.
6*
7* nTorrent is free software: you can redistribute it and/or modify it under the
8* terms of the GNU Lesser General Public License as published by the Free Software
9* Foundation, either version 3 of the License, or (at your option) any later version.
10*
11* nTorrent is distributed in the hope that it will be useful, but WITHOUT ANY
12* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
13* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
14*
15* You should have received copies of the GNU General Public License and GNU Lesser
16* General Public License along with nTorrent, e.g., in COPYING.md file. If not, see
17* <http://www.gnu.org/licenses/>.
18*
19* See AUTHORS for complete list of nTorrent authors and contributors.
20*/
21
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -070022#include "util/io-util.hpp"
23
24#include "file-manifest.hpp"
25#include "torrent-file.hpp"
Mickey Sweatt617d2d42016-04-25 22:02:08 -070026#include "util/logging.hpp"
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -070027
28#include <boost/filesystem.hpp>
29#include <boost/filesystem/fstream.hpp>
30
31#include <ndn-cxx/security/key-chain.hpp>
32#include <ndn-cxx/security/signing-helpers.hpp>
33
34namespace fs = boost::filesystem;
35
36using std::string;
37using std::vector;
38
39namespace ndn {
40namespace ntorrent {
41
42std::vector<ndn::Data>
43IoUtil::packetize_file(const fs::path& filePath,
44 const ndn::Name& commonPrefix,
45 size_t dataPacketSize,
46 size_t subManifestSize,
47 size_t subManifestNum)
48{
49 BOOST_ASSERT(0 < dataPacketSize);
50 size_t APPROX_BUFFER_SIZE = std::numeric_limits<int>::max(); // 2 * 1024 * 1024 *1024
51 auto file_size = fs::file_size(filePath);
52 auto start_offset = subManifestNum * subManifestSize * dataPacketSize;
53 // determine the number of bytes in this submanifest
54 auto subManifestLength = subManifestSize * dataPacketSize;
55 auto remainingFileLength = file_size - start_offset;
56 subManifestLength = remainingFileLength < subManifestLength
57 ? remainingFileLength
58 : subManifestLength;
59 vector<ndn::Data> packets;
60 packets.reserve(subManifestLength/dataPacketSize + 1);
61 fs::ifstream fs(filePath, fs::ifstream::binary);
62 if (!fs) {
63 BOOST_THROW_EXCEPTION(Data::Error("IO Error when opening" + filePath.string()));
64 }
65 // ensure that buffer is large enough to contain whole packets
66 // buffer size is either the entire file or the smallest number of data packets >= 2 GB
67 auto buffer_size =
68 subManifestLength < APPROX_BUFFER_SIZE ?
69 subManifestLength :
70 APPROX_BUFFER_SIZE % dataPacketSize == 0 ?
71 APPROX_BUFFER_SIZE :
72 APPROX_BUFFER_SIZE + dataPacketSize - (APPROX_BUFFER_SIZE % dataPacketSize);
73 vector<char> file_bytes;
74 file_bytes.reserve(buffer_size);
75 size_t bytes_read = 0;
76 fs.seekg(start_offset);
77 while(fs && bytes_read < subManifestLength && !fs.eof()) {
78 // read the file into the buffer
79 fs.read(&file_bytes.front(), buffer_size);
80 auto read_size = fs.gcount();
81 if (fs.bad() || read_size < 0) {
82 BOOST_THROW_EXCEPTION(Data::Error("IO Error when reading" + filePath.string()));
83 }
84 bytes_read += read_size;
85 char *curr_start = &file_bytes.front();
86 for (size_t i = 0u; i < buffer_size; i += dataPacketSize) {
87 // Build a packet from the data
88 Name packetName = commonPrefix;
89 packetName.appendSequenceNumber(packets.size());
90 Data d(packetName);
91 auto content_length = i + dataPacketSize > buffer_size ? buffer_size - i : dataPacketSize;
92 d.setContent(encoding::makeBinaryBlock(tlv::Content, curr_start, content_length));
93 curr_start += content_length;
94 // append to the collection
95 packets.push_back(d);
96 }
97 file_bytes.clear();
98 // recompute the buffer_size
99 buffer_size =
100 subManifestLength - bytes_read < APPROX_BUFFER_SIZE ?
101 subManifestLength - bytes_read :
102 APPROX_BUFFER_SIZE % dataPacketSize == 0 ?
103 APPROX_BUFFER_SIZE :
104 APPROX_BUFFER_SIZE + dataPacketSize - (APPROX_BUFFER_SIZE % dataPacketSize);
105 }
106 fs.close();
107 packets.shrink_to_fit();
108 ndn::security::KeyChain key_chain;
109 // sign all the packets
110 for (auto& p : packets) {
111 key_chain.sign(p, signingWithSha256());
112 }
113 return packets;
114}
115
116bool IoUtil::writeTorrentSegment(const TorrentFile& segment, const std::string& path)
117{
118 // validate that this torrent segment belongs to our torrent
119 auto segmentNum = segment.getSegmentNumber();
120 // write to disk at path
121 if (!fs::exists(path)) {
122 fs::create_directories(path);
123 }
124 auto filename = path + to_string(segmentNum);
125 // if there is already a file on disk for this torrent segment, determine if we should override
126 if (fs::exists(filename)) {
127 auto segmentOnDisk_ptr = io::load<TorrentFile>(filename);
128 if (nullptr != segmentOnDisk_ptr && *segmentOnDisk_ptr == segment) {
129 return false;
130 }
131 }
132 io::save(segment, filename);
133 // add to collection
134 return true;
135}
136
137bool IoUtil::writeFileManifest(const FileManifest& manifest, const std::string& path)
138{
139 auto subManifestNum = manifest.submanifest_number();
140 fs::path filename = path + manifest.file_name() + "/" + to_string(subManifestNum);
141
142 // write to disk at path
143 if (!fs::exists(filename.parent_path())) {
144 boost::filesystem::create_directories(filename.parent_path());
145 }
146 // if there is already a file on disk for this file manifest, determine if we should override
147 if (fs::exists(filename)) {
148 auto submanifestOnDisk_ptr = io::load<FileManifest>(filename.string());
149 if (nullptr != submanifestOnDisk_ptr && *submanifestOnDisk_ptr == manifest) {
150 return false;
151 }
152 }
153 io::save(manifest, filename.string());
154 return true;
155}
156bool
157IoUtil::writeData(const Data& packet, const FileManifest& manifest, size_t subManifestSize, fs::fstream& os)
158{
159 auto packetName = packet.getName();
160 auto packetNum = packetName.get(packetName.size() - 1).toSequenceNumber();
161 auto dataPacketSize = manifest.data_packet_size();
162 auto initial_offset = manifest.submanifest_number() * subManifestSize * dataPacketSize;
163 auto packetOffset = initial_offset + packetNum * dataPacketSize;
164 // write data to disk
165 os.seekp(packetOffset);
166 try {
167 auto content = packet.getContent();
168 std::vector<char> data(content.value_begin(), content.value_end());
169 os.write(&data[0], data.size());
170 return true;
171 }
172 catch (io::Error &e) {
Mickey Sweatt617d2d42016-04-25 22:02:08 -0700173 LOG_ERROR << e.what() << std::endl;
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -0700174 return false;
175 }
176}
177
178std::shared_ptr<Data>
179IoUtil::readDataPacket(const Name& packetFullName,
180 const FileManifest& manifest,
181 size_t subManifestSize,
182 fs::fstream& is)
183{
184 auto dataPacketSize = manifest.data_packet_size();
185 auto start_offset = manifest.submanifest_number() * subManifestSize * dataPacketSize;
186 auto packetNum = packetFullName.get(packetFullName.size() - 2).toSequenceNumber();
187 // seek to packet
188 is.sync();
189 is.seekg(start_offset + packetNum * dataPacketSize);
190 if (is.tellg() < 0) {
Mickey Sweatt617d2d42016-04-25 22:02:08 -0700191 LOG_ERROR << "bad seek" << std::endl;
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -0700192 }
193 // read contents
194 std::vector<char> bytes(dataPacketSize);
195 is.read(&bytes.front(), dataPacketSize);
196 auto read_size = is.gcount();
197 if (is.bad() || read_size < 0) {
Mickey Sweatt617d2d42016-04-25 22:02:08 -0700198 LOG_ERROR << "Bad read" << std::endl;
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -0700199 return nullptr;
200 }
201 // construct packet
202 auto packetName = packetFullName.getSubName(0, packetFullName.size() - 1);
203 auto d = make_shared<Data>(packetName);
204 d->setContent(encoding::makeBinaryBlock(tlv::Content, &bytes.front(), read_size));
205 ndn::security::KeyChain key_chain;
206 key_chain.sign(*d, signingWithSha256());
207 return d->getFullName() == packetFullName ? d : nullptr;
208}
209
210IoUtil::NAME_TYPE
211IoUtil::findType(const Name& name)
212{
213 NAME_TYPE rval = UNKNOWN;
214 if (name.get(name.size() - 2).toUri() == "torrent-file" ||
215 name.get(name.size() - 3).toUri() == "torrent-file") {
216 rval = TORRENT_FILE;
217 }
218 else if (name.get(name.size() - 2).isSequenceNumber() &&
219 name.get(name.size() - 3).isSequenceNumber()) {
220 rval = DATA_PACKET;
221 }
222 else if (name.get(name.size() - 2).isSequenceNumber() &&
223 !(name.get(name.size() - 3).isSequenceNumber())) {
224 rval = FILE_MANIFEST;
225 }
226 return rval;
227}
228
229
230
231} // namespace ntorrent
232} // namespace ndn}