blob: 12572b3bb0eb6f85980ed00e4f2f8835b52bc61d [file] [log] [blame]
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -07001#include "util/io-util.hpp"
2
3#include "file-manifest.hpp"
4#include "torrent-file.hpp"
Mickey Sweatt617d2d42016-04-25 22:02:08 -07005#include "util/logging.hpp"
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -07006
7#include <boost/filesystem.hpp>
8#include <boost/filesystem/fstream.hpp>
9
10#include <ndn-cxx/security/key-chain.hpp>
11#include <ndn-cxx/security/signing-helpers.hpp>
12
13namespace fs = boost::filesystem;
14
15using std::string;
16using std::vector;
17
18namespace ndn {
19namespace ntorrent {
20
21std::vector<ndn::Data>
22IoUtil::packetize_file(const fs::path& filePath,
23 const ndn::Name& commonPrefix,
24 size_t dataPacketSize,
25 size_t subManifestSize,
26 size_t subManifestNum)
27{
28 BOOST_ASSERT(0 < dataPacketSize);
29 size_t APPROX_BUFFER_SIZE = std::numeric_limits<int>::max(); // 2 * 1024 * 1024 *1024
30 auto file_size = fs::file_size(filePath);
31 auto start_offset = subManifestNum * subManifestSize * dataPacketSize;
32 // determine the number of bytes in this submanifest
33 auto subManifestLength = subManifestSize * dataPacketSize;
34 auto remainingFileLength = file_size - start_offset;
35 subManifestLength = remainingFileLength < subManifestLength
36 ? remainingFileLength
37 : subManifestLength;
38 vector<ndn::Data> packets;
39 packets.reserve(subManifestLength/dataPacketSize + 1);
40 fs::ifstream fs(filePath, fs::ifstream::binary);
41 if (!fs) {
42 BOOST_THROW_EXCEPTION(Data::Error("IO Error when opening" + filePath.string()));
43 }
44 // ensure that buffer is large enough to contain whole packets
45 // buffer size is either the entire file or the smallest number of data packets >= 2 GB
46 auto buffer_size =
47 subManifestLength < APPROX_BUFFER_SIZE ?
48 subManifestLength :
49 APPROX_BUFFER_SIZE % dataPacketSize == 0 ?
50 APPROX_BUFFER_SIZE :
51 APPROX_BUFFER_SIZE + dataPacketSize - (APPROX_BUFFER_SIZE % dataPacketSize);
52 vector<char> file_bytes;
53 file_bytes.reserve(buffer_size);
54 size_t bytes_read = 0;
55 fs.seekg(start_offset);
56 while(fs && bytes_read < subManifestLength && !fs.eof()) {
57 // read the file into the buffer
58 fs.read(&file_bytes.front(), buffer_size);
59 auto read_size = fs.gcount();
60 if (fs.bad() || read_size < 0) {
61 BOOST_THROW_EXCEPTION(Data::Error("IO Error when reading" + filePath.string()));
62 }
63 bytes_read += read_size;
64 char *curr_start = &file_bytes.front();
65 for (size_t i = 0u; i < buffer_size; i += dataPacketSize) {
66 // Build a packet from the data
67 Name packetName = commonPrefix;
68 packetName.appendSequenceNumber(packets.size());
69 Data d(packetName);
70 auto content_length = i + dataPacketSize > buffer_size ? buffer_size - i : dataPacketSize;
71 d.setContent(encoding::makeBinaryBlock(tlv::Content, curr_start, content_length));
72 curr_start += content_length;
73 // append to the collection
74 packets.push_back(d);
75 }
76 file_bytes.clear();
77 // recompute the buffer_size
78 buffer_size =
79 subManifestLength - bytes_read < APPROX_BUFFER_SIZE ?
80 subManifestLength - bytes_read :
81 APPROX_BUFFER_SIZE % dataPacketSize == 0 ?
82 APPROX_BUFFER_SIZE :
83 APPROX_BUFFER_SIZE + dataPacketSize - (APPROX_BUFFER_SIZE % dataPacketSize);
84 }
85 fs.close();
86 packets.shrink_to_fit();
87 ndn::security::KeyChain key_chain;
88 // sign all the packets
89 for (auto& p : packets) {
90 key_chain.sign(p, signingWithSha256());
91 }
92 return packets;
93}
94
95bool IoUtil::writeTorrentSegment(const TorrentFile& segment, const std::string& path)
96{
97 // validate that this torrent segment belongs to our torrent
98 auto segmentNum = segment.getSegmentNumber();
99 // write to disk at path
100 if (!fs::exists(path)) {
101 fs::create_directories(path);
102 }
103 auto filename = path + to_string(segmentNum);
104 // if there is already a file on disk for this torrent segment, determine if we should override
105 if (fs::exists(filename)) {
106 auto segmentOnDisk_ptr = io::load<TorrentFile>(filename);
107 if (nullptr != segmentOnDisk_ptr && *segmentOnDisk_ptr == segment) {
108 return false;
109 }
110 }
111 io::save(segment, filename);
112 // add to collection
113 return true;
114}
115
116bool IoUtil::writeFileManifest(const FileManifest& manifest, const std::string& path)
117{
118 auto subManifestNum = manifest.submanifest_number();
119 fs::path filename = path + manifest.file_name() + "/" + to_string(subManifestNum);
120
121 // write to disk at path
122 if (!fs::exists(filename.parent_path())) {
123 boost::filesystem::create_directories(filename.parent_path());
124 }
125 // if there is already a file on disk for this file manifest, determine if we should override
126 if (fs::exists(filename)) {
127 auto submanifestOnDisk_ptr = io::load<FileManifest>(filename.string());
128 if (nullptr != submanifestOnDisk_ptr && *submanifestOnDisk_ptr == manifest) {
129 return false;
130 }
131 }
132 io::save(manifest, filename.string());
133 return true;
134}
135bool
136IoUtil::writeData(const Data& packet, const FileManifest& manifest, size_t subManifestSize, fs::fstream& os)
137{
138 auto packetName = packet.getName();
139 auto packetNum = packetName.get(packetName.size() - 1).toSequenceNumber();
140 auto dataPacketSize = manifest.data_packet_size();
141 auto initial_offset = manifest.submanifest_number() * subManifestSize * dataPacketSize;
142 auto packetOffset = initial_offset + packetNum * dataPacketSize;
143 // write data to disk
144 os.seekp(packetOffset);
145 try {
146 auto content = packet.getContent();
147 std::vector<char> data(content.value_begin(), content.value_end());
148 os.write(&data[0], data.size());
149 return true;
150 }
151 catch (io::Error &e) {
Mickey Sweatt617d2d42016-04-25 22:02:08 -0700152 LOG_ERROR << e.what() << std::endl;
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -0700153 return false;
154 }
155}
156
157std::shared_ptr<Data>
158IoUtil::readDataPacket(const Name& packetFullName,
159 const FileManifest& manifest,
160 size_t subManifestSize,
161 fs::fstream& is)
162{
163 auto dataPacketSize = manifest.data_packet_size();
164 auto start_offset = manifest.submanifest_number() * subManifestSize * dataPacketSize;
165 auto packetNum = packetFullName.get(packetFullName.size() - 2).toSequenceNumber();
166 // seek to packet
167 is.sync();
168 is.seekg(start_offset + packetNum * dataPacketSize);
169 if (is.tellg() < 0) {
Mickey Sweatt617d2d42016-04-25 22:02:08 -0700170 LOG_ERROR << "bad seek" << std::endl;
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -0700171 }
172 // read contents
173 std::vector<char> bytes(dataPacketSize);
174 is.read(&bytes.front(), dataPacketSize);
175 auto read_size = is.gcount();
176 if (is.bad() || read_size < 0) {
Mickey Sweatt617d2d42016-04-25 22:02:08 -0700177 LOG_ERROR << "Bad read" << std::endl;
Mickey Sweattfcbfb3d2016-04-13 17:05:17 -0700178 return nullptr;
179 }
180 // construct packet
181 auto packetName = packetFullName.getSubName(0, packetFullName.size() - 1);
182 auto d = make_shared<Data>(packetName);
183 d->setContent(encoding::makeBinaryBlock(tlv::Content, &bytes.front(), read_size));
184 ndn::security::KeyChain key_chain;
185 key_chain.sign(*d, signingWithSha256());
186 return d->getFullName() == packetFullName ? d : nullptr;
187}
188
189IoUtil::NAME_TYPE
190IoUtil::findType(const Name& name)
191{
192 NAME_TYPE rval = UNKNOWN;
193 if (name.get(name.size() - 2).toUri() == "torrent-file" ||
194 name.get(name.size() - 3).toUri() == "torrent-file") {
195 rval = TORRENT_FILE;
196 }
197 else if (name.get(name.size() - 2).isSequenceNumber() &&
198 name.get(name.size() - 3).isSequenceNumber()) {
199 rval = DATA_PACKET;
200 }
201 else if (name.get(name.size() - 2).isSequenceNumber() &&
202 !(name.get(name.size() - 3).isSequenceNumber())) {
203 rval = FILE_MANIFEST;
204 }
205 return rval;
206}
207
208
209
210} // namespace ntorrent
211} // namespace ndn}