1 /*****************************************************************************\
3 * Name : recursive_file_copy *
4 * Author : Chris Koeritz *
6 *******************************************************************************
7 * Copyright (c) 2005-$now By Author. This program is free software; you can *
8 * redistribute it and/or modify it under the terms of the GNU General Public *
9 * License as published by the Free Software Foundation; either version 2 of *
10 * the License or (at your option) any later version. This is online at: *
11 * http://www.fsf.org/copyleft/gpl.html *
12 * Please send any updates to: fred@gruntose.com *
13 \*****************************************************************************/
15 #include "file_transfer_infoton.h"
16 #include "file_transfer_tentacle.h"
17 #include "recursive_file_copy.h"
19 #include <application/application_shell.h>
20 #include <basis/guards.h>
21 #include <filesystem/directory.h>
22 #include <filesystem/directory_tree.h>
23 #include <filesystem/filename.h>
24 #include <filesystem/filename_list.h>
25 #include <filesystem/heavy_file_ops.h>
26 #include <filesystem/huge_file.h>
27 #include <loggers/program_wide_logger.h>
28 #include <octopus/entity_defs.h>
29 #include <octopus/entity_data_bin.h>
30 #include <octopus/octopus.h>
31 #include <structures/static_memory_gremlin.h>
32 #include <textual/string_manipulation.h>
33 #include <timely/time_control.h>
35 using namespace application;
36 using namespace basis;
37 using namespace filesystem;
38 using namespace loggers;
39 using namespace structures;
40 using namespace textual;
41 using namespace timely;
45 #define DEBUG_RECURSIVE_FILE_COPY
46 // uncomment for noisier debugging.
48 #define FAKE_HOSTNAME "internal_fake_host"
51 #define LOG(s) CLASS_EMERGENCY_LOG(program_wide_logger::get(), s)
53 #define BASE_LOG(s) EMERGENCY_LOG(program_wide_logger::get(), s)
55 #define RETURN_ERROR_RFC(msg, err) { \
60 const int MAX_CHUNK_RFC_COPY_HIER = 1 * MEGABYTE;
61 // maximum size for each transfer chunk.
63 const int EXPECTED_MAXIMUM_TRANSFER_TIME = 10 * HOUR_ms;
64 // how long do we allow the scanned file lists to stay relevant.
65 // we allow it a long time, since this is a copy and not an active
68 recursive_file_copy::~recursive_file_copy() {}
70 const char *recursive_file_copy::outcome_name(const outcome &to_name)
71 { return common::outcome_name(to_name); }
73 outcome recursive_file_copy::copy_hierarchy(int transfer_mode,
74 const astring &source_dir, const astring &target_dir,
75 const string_array &includes, const astring &source_start)
77 FUNCDEF("copy_hierarchy");
80 string_array includes;
81 if (_global_argc >= 5) {
82 for (int i = 4; i < _global_argc; i++) {
83 includes += _global_argv[i];
88 astring source_root = "snootums";
89 if (source_start.t()) {
90 source_root += filename::default_separator() + source_start;
93 octopus ring_leader(FAKE_HOSTNAME, 10 * MEGABYTE);
94 file_transfer_tentacle *tran = new file_transfer_tentacle
95 (MAX_CHUNK_RFC_COPY_HIER, (file_transfer_tentacle::transfer_modes)transfer_mode);
96 ring_leader.add_tentacle(tran);
98 outcome add_ret = tran->add_correspondence("snootums", source_dir,
99 EXPECTED_MAXIMUM_TRANSFER_TIME);
100 if (add_ret != tentacle::OKAY)
101 RETURN_ERROR_RFC("failed to add the correspondence", NOT_FOUND);
103 file_transfer_infoton *initiate = new file_transfer_infoton;
104 initiate->_request = true;
105 initiate->_command = file_transfer_infoton::TREE_COMPARISON;
106 initiate->_src_root = source_root;
107 initiate->_dest_root = target_dir;
108 directory_tree target_area(target_dir);
109 //hmmm: simple asset counting debugging in calculate would be nice too.
110 target_area.calculate( !(transfer_mode & file_transfer_tentacle::COMPARE_CONTENT_SAMPLE) );
111 initiate->package_tree_info(target_area, includes);
113 octopus_entity ent = ring_leader.issue_identity();
114 octopus_request_id req_id(ent, 1);
115 outcome start_ret = ring_leader.evaluate(initiate, req_id);
116 if (start_ret != tentacle::OKAY)
117 RETURN_ERROR_RFC("failed to start the comparison", NONE_READY);
119 file_transfer_infoton *reply_from_init
120 = (file_transfer_infoton *)ring_leader.acquire_specific_result(req_id);
121 if (!reply_from_init) {
122 LOG("spewing list of what IS there...");
123 LOG(ring_leader.responses().text_form());
124 RETURN_ERROR_RFC("no response to tree compare start", NONE_READY);
128 byte_array pack_copy = reply_from_init->_packed_data;
129 if (!diffs.unpack(pack_copy)) {
130 RETURN_ERROR_RFC("could not unpack filename list!", GARBAGE);
132 // LOG(astring("got list of diffs:\n") + diffs.text_form());
134 octopus client_spider(FAKE_HOSTNAME, 10 * MEGABYTE);
135 file_transfer_tentacle *tran2 = new file_transfer_tentacle
136 (MAX_CHUNK_RFC_COPY_HIER, (file_transfer_tentacle::transfer_modes)transfer_mode);
137 tran2->register_file_transfer(ent, source_root, target_dir, includes);
138 client_spider.add_tentacle(tran2);
140 octopus_request_id resp_id(ent, 2);
141 outcome ini_resp_ret = client_spider.evaluate(reply_from_init, resp_id);
142 if (ini_resp_ret != tentacle::OKAY)
143 RETURN_ERROR_RFC("failed to process the start response!", FAILURE);
145 infoton *junk = client_spider.acquire_specific_result(resp_id);
147 RETURN_ERROR_RFC("got a response we shouldn't have!", FAILURE);
149 astring current_file; // what file is in progress right now?
153 #ifdef DEBUG_RECURSIVE_FILE_COPY
154 LOG(a_sprintf("ongoing chunk %d", ++iter));
157 // keep going until we find a broken reply.
158 file_transfer_infoton *ongoing = new file_transfer_infoton;
159 ongoing->_request = true;
160 ongoing->_command = file_transfer_infoton::PLACE_FILE_CHUNKS;
161 ongoing->_src_root = source_root;
162 ongoing->_dest_root = target_dir;
164 octopus_request_id chunk_id(ent, iter + 10);
165 outcome place_ret = ring_leader.evaluate(ongoing, chunk_id);
166 if (place_ret != tentacle::OKAY)
167 RETURN_ERROR_RFC("failed to run ongoing transfer", FAILURE);
169 file_transfer_infoton *reply = (file_transfer_infoton *)ring_leader
170 .acquire_specific_result(chunk_id);
172 RETURN_ERROR_RFC("failed to get ongoing transfer reply", NONE_READY);
174 if (reply->_command == file_transfer_infoton::CONCLUDE_TRANSFER_MARKER) {
175 BASE_LOG(astring("finished transfer from \"") + source_dir
176 + "\" to \"" + target_dir + "\"");
180 byte_array copy = reply->_packed_data;
181 while (copy.length()) {
183 file_transfer_header head(empty);
184 if (!head.unpack(copy))
185 RETURN_ERROR_RFC("failed to unpack header", GARBAGE);
186 if (copy.length() < head._length)
187 RETURN_ERROR_RFC("not enough length in array", GARBAGE);
188 if (head._length > 0)
189 copy.zap(0, head._length - 1);
191 //hmmm: this needs better formatting, and should not repeat the same file name even
192 // if it's in multiple chunks.
193 BASE_LOG(head.readable_text_form());
196 RETURN_ERROR_RFC("still had data in array", GARBAGE);
198 octopus_request_id resp_id(ent, iter + 11);
199 outcome resp_ret = client_spider.evaluate(reply, resp_id);
200 if (resp_ret != tentacle::OKAY)
201 RETURN_ERROR_RFC("failed to process the transfer reply!", FAILURE);