053-scan_task.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. // SPDX-FileCopyrightText: 2019-2023 Ivan Baidakou
  3. #include "test-utils.h"
  4. #include "fs/scan_task.h"
  5. using namespace syncspirit;
  6. using namespace syncspirit::test;
  7. using namespace syncspirit::utils;
  8. using namespace syncspirit::model;
  9. using namespace syncspirit::fs;
  10. TEST_CASE("scan_task", "[fs]") {
  11. utils::set_default("trace");
  12. auto root_path = bfs::unique_path();
  13. bfs::create_directories(root_path);
  14. path_guard_t path_quard{root_path};
  15. config::fs_config_t config{3600, 10};
  16. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  17. auto my_device = device_t::create(my_id, "my-device").value();
  18. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  19. auto peer_device = device_t::create(peer_id, "peer-device").value();
  20. auto cluster = cluster_ptr_t(new cluster_t(my_device, 1, 1));
  21. cluster->get_devices().put(my_device);
  22. cluster->get_devices().put(peer_device);
  23. auto db_folder = db::Folder();
  24. db_folder.set_id("some-id");
  25. db_folder.set_label("zzz");
  26. db_folder.set_path(root_path.string());
  27. auto folder = folder_t::create(cluster->next_uuid(), db_folder).value();
  28. cluster->get_folders().put(folder);
  29. db::FolderInfo db_folder_info;
  30. db_folder_info.set_index_id(1234);
  31. db_folder_info.set_max_sequence(3);
  32. auto folder_my = folder_info_t::create(cluster->next_uuid(), db_folder_info, my_device, folder).value();
  33. auto folder_peer = folder_info_t::create(cluster->next_uuid(), db_folder_info, peer_device, folder).value();
  34. folder->get_folder_infos().put(folder_my);
  35. folder->get_folder_infos().put(folder_peer);
  36. SECTION("without files") {
  37. #ifndef SYNCSPIRIT_WIN
  38. SECTION("no permissions to read dir => err") {
  39. bfs::permissions(root_path, bfs::perms::no_perms);
  40. auto folder_info = folder_info_t::create(cluster->next_uuid(), db_folder_info, my_device, folder).value();
  41. folder->get_folder_infos().put(folder_info);
  42. auto task = scan_task_t(cluster, folder->get_id(), config);
  43. auto r = task.advance();
  44. CHECK(std::get_if<io_errors_t>(&r));
  45. auto errs = std::get_if<io_errors_t>(&r);
  46. REQUIRE(errs->size() == 1);
  47. auto &err = errs->at(0);
  48. CHECK(err.ec);
  49. CHECK(err.path == root_path);
  50. }
  51. #endif
  52. SECTION("no dirs, no files") {
  53. auto task = scan_task_t(cluster, folder->get_id(), config);
  54. auto r = task.advance();
  55. CHECK(std::get_if<bool>(&r));
  56. CHECK(*std::get_if<bool>(&r) == true);
  57. r = task.advance();
  58. CHECK(std::get_if<bool>(&r));
  59. CHECK(*std::get_if<bool>(&r) == false);
  60. }
  61. SECTION("some dirs, no files") {
  62. auto task = scan_task_t(cluster, folder->get_id(), config);
  63. auto dir = root_path / "some-dir";
  64. bfs::create_directories(dir);
  65. auto r = task.advance();
  66. CHECK(std::get_if<bool>(&r));
  67. CHECK(*std::get_if<bool>(&r) == true);
  68. r = task.advance();
  69. CHECK(std::get_if<bool>(&r));
  70. CHECK(*std::get_if<bool>(&r) == true);
  71. r = task.advance();
  72. CHECK(std::get_if<bool>(&r));
  73. CHECK(*std::get_if<bool>(&r) == false);
  74. }
  75. SECTION("no dirs, unknown files") {
  76. auto task = scan_task_t(cluster, folder->get_id(), config);
  77. write_file(root_path / "some-file", "");
  78. auto r = task.advance();
  79. CHECK(std::get_if<bool>(&r));
  80. CHECK(*std::get_if<bool>(&r) == true);
  81. r = task.advance();
  82. auto *uf = std::get_if<unknown_file_t>(&r);
  83. REQUIRE(uf);
  84. CHECK(uf->path.filename() == "some-file");
  85. CHECK(uf->metadata.size() == 0);
  86. CHECK(uf->metadata.type() == proto::FileInfoType::FILE);
  87. r = task.advance();
  88. REQUIRE(std::get_if<bool>(&r));
  89. CHECK(*std::get_if<bool>(&r) == false);
  90. }
  91. SECTION("no dirs, symlinks") {
  92. auto task = scan_task_t(cluster, folder->get_id(), config);
  93. auto file_path = root_path / "symlink";
  94. bfs::create_symlink(bfs::path("/some/where"), file_path);
  95. auto r = task.advance();
  96. CHECK(std::get_if<bool>(&r));
  97. CHECK(*std::get_if<bool>(&r) == true);
  98. r = task.advance();
  99. auto *uf = std::get_if<unknown_file_t>(&r);
  100. REQUIRE(uf);
  101. CHECK(uf->path.filename() == "symlink");
  102. CHECK(uf->metadata.size() == 0);
  103. CHECK(uf->metadata.type() == proto::FileInfoType::SYMLINK);
  104. CHECK(uf->metadata.symlink_target() == "/some/where");
  105. r = task.advance();
  106. REQUIRE(std::get_if<bool>(&r));
  107. CHECK(*std::get_if<bool>(&r) == false);
  108. }
  109. }
  110. SECTION("regular files") {
  111. auto modified = std::time_t{1642007468};
  112. auto pr_file = proto::FileInfo{};
  113. pr_file.set_name("a.txt");
  114. pr_file.set_sequence(2);
  115. auto version = pr_file.mutable_version();
  116. auto counter = version->add_counters();
  117. counter->set_id(1);
  118. counter->set_value(peer_device->as_uint());
  119. SECTION("meta is not changed") {
  120. pr_file.set_block_size(5);
  121. pr_file.set_size(5);
  122. pr_file.set_modified_s(modified);
  123. auto path = root_path / "a.txt";
  124. write_file(path, "12345");
  125. bfs::last_write_time(path, modified);
  126. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  127. folder_my->add(file, false);
  128. auto task = scan_task_t(cluster, folder->get_id(), config);
  129. auto r = task.advance();
  130. CHECK(std::get_if<bool>(&r));
  131. CHECK(*std::get_if<bool>(&r) == true);
  132. r = task.advance();
  133. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  134. auto ref = std::get_if<unchanged_meta_t>(&r);
  135. CHECK(ref->file == file);
  136. r = task.advance();
  137. CHECK(std::get_if<bool>(&r));
  138. CHECK(*std::get_if<bool>(&r) == false);
  139. }
  140. SECTION("file has been removed") {
  141. pr_file.set_block_size(5);
  142. pr_file.set_size(5);
  143. pr_file.set_modified_s(modified);
  144. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  145. folder_my->add(file, false);
  146. auto task = scan_task_t(cluster, folder->get_id(), config);
  147. auto r = task.advance();
  148. CHECK(std::get_if<bool>(&r));
  149. CHECK(*std::get_if<bool>(&r) == true);
  150. r = task.advance();
  151. REQUIRE(std::get_if<removed_t>(&r));
  152. auto ref = std::get_if<removed_t>(&r);
  153. CHECK(ref->file == file);
  154. r = task.advance();
  155. CHECK(std::get_if<bool>(&r));
  156. CHECK(*std::get_if<bool>(&r) == false);
  157. }
  158. SECTION("removed file does not exist => unchanged meta") {
  159. pr_file.set_deleted(true);
  160. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  161. folder_my->add(file, false);
  162. auto task = scan_task_t(cluster, folder->get_id(), config);
  163. auto r = task.advance();
  164. CHECK(std::get_if<bool>(&r));
  165. CHECK(*std::get_if<bool>(&r) == true);
  166. r = task.advance();
  167. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  168. auto ref = std::get_if<unchanged_meta_t>(&r);
  169. CHECK(ref->file == file);
  170. r = task.advance();
  171. CHECK(std::get_if<bool>(&r));
  172. CHECK(*std::get_if<bool>(&r) == false);
  173. }
  174. SECTION("root dir does not exist & deleted file => unchanged meta") {
  175. pr_file.set_deleted(true);
  176. folder->set_path(root_path / "zzz");
  177. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  178. folder_my->add(file, false);
  179. auto task = scan_task_t(cluster, folder->get_id(), config);
  180. auto r = task.advance();
  181. REQUIRE(std::get_if<bool>(&r));
  182. CHECK(*std::get_if<bool>(&r) == true);
  183. r = task.advance();
  184. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  185. auto ref = std::get_if<unchanged_meta_t>(&r);
  186. CHECK(ref->file == file);
  187. r = task.advance();
  188. CHECK(std::get_if<bool>(&r));
  189. CHECK(*std::get_if<bool>(&r) == false);
  190. }
  191. SECTION("meta is changed") {
  192. auto task = scan_task_ptr_t{};
  193. auto file = file_info_ptr_t{};
  194. SECTION("file size differs") {
  195. pr_file.set_block_size(5);
  196. pr_file.set_size(6);
  197. pr_file.set_modified_s(modified);
  198. auto path = root_path / "a.txt";
  199. write_file(path, "12345");
  200. bfs::last_write_time(path, modified);
  201. file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  202. folder_my->add(file, false);
  203. }
  204. SECTION("modification time differs") {
  205. pr_file.set_block_size(5);
  206. pr_file.set_size(5);
  207. pr_file.set_modified_s(modified + 1);
  208. auto path = root_path / "a.txt";
  209. write_file(path, "12345");
  210. bfs::last_write_time(path, modified);
  211. file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  212. folder_my->add(file, false);
  213. }
  214. task = new scan_task_t(cluster, folder->get_id(), config);
  215. auto r = task->advance();
  216. CHECK(std::get_if<bool>(&r));
  217. CHECK(*std::get_if<bool>(&r) == true);
  218. r = task->advance();
  219. REQUIRE(std::get_if<changed_meta_t>(&r));
  220. auto ref = std::get_if<changed_meta_t>(&r);
  221. CHECK(ref->file == file);
  222. r = task->advance();
  223. CHECK(std::get_if<bool>(&r));
  224. CHECK(*std::get_if<bool>(&r) == false);
  225. }
  226. SECTION("tmp") {
  227. pr_file.set_block_size(5);
  228. pr_file.set_size(5);
  229. pr_file.set_modified_s(modified);
  230. auto path = root_path / "a.txt.syncspirit-tmp";
  231. SECTION("size match -> ok, will recalc") {
  232. write_file(path, "12345");
  233. auto file_my = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  234. auto file_peer = file_info_t::create(cluster->next_uuid(), pr_file, folder_peer).value();
  235. folder_my->add(file_my, false);
  236. folder_peer->add(file_peer, false);
  237. file_my->set_source(file_peer);
  238. auto task = scan_task_t(cluster, folder->get_id(), config);
  239. auto r = task.advance();
  240. CHECK(std::get_if<bool>(&r));
  241. CHECK(*std::get_if<bool>(&r) == true);
  242. r = task.advance();
  243. REQUIRE(std::get_if<incomplete_t>(&r));
  244. auto ref = std::get_if<incomplete_t>(&r);
  245. CHECK(ref->file);
  246. CHECK(ref->opened_file);
  247. r = task.advance();
  248. CHECK(std::get_if<bool>(&r));
  249. CHECK(*std::get_if<bool>(&r) == false);
  250. }
  251. SECTION("source is missing") {
  252. write_file(path, "12345");
  253. auto file_my = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  254. folder_my->add(file_my, false);
  255. auto task = scan_task_t(cluster, folder->get_id(), config);
  256. auto r = task.advance();
  257. CHECK(std::get_if<bool>(&r));
  258. CHECK(*std::get_if<bool>(&r) == true);
  259. r = task.advance();
  260. CHECK(std::get_if<incomplete_removed_t>(&r));
  261. CHECK(std::get_if<incomplete_removed_t>(&r)->file == file_my);
  262. r = task.advance();
  263. CHECK(std::get_if<bool>(&r));
  264. CHECK(*std::get_if<bool>(&r) == false);
  265. }
  266. SECTION("size mismatch -> remove & ignore") {
  267. write_file(path, "123456");
  268. auto file_my = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  269. auto file_peer = file_info_t::create(cluster->next_uuid(), pr_file, folder_peer).value();
  270. folder_my->add(file_my, false);
  271. folder_peer->add(file_peer, false);
  272. file_my->set_source(file_peer);
  273. auto task = scan_task_t(cluster, folder->get_id(), config);
  274. auto r = task.advance();
  275. CHECK(std::get_if<bool>(&r));
  276. CHECK(*std::get_if<bool>(&r) == true);
  277. r = task.advance();
  278. CHECK(std::get_if<incomplete_removed_t>(&r));
  279. CHECK(std::get_if<incomplete_removed_t>(&r)->file == file_my);
  280. r = task.advance();
  281. CHECK(std::get_if<bool>(&r));
  282. CHECK(*std::get_if<bool>(&r) == false);
  283. CHECK(!bfs::exists(path));
  284. }
  285. }
  286. SECTION("tmp & non-tmp: both are returned") {
  287. pr_file.set_block_size(5);
  288. pr_file.set_size(5);
  289. pr_file.set_modified_s(modified);
  290. auto file_my = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  291. counter->set_id(10);
  292. auto file_peer = file_info_t::create(cluster->next_uuid(), pr_file, folder_peer).value();
  293. folder_my->add(file_my, false);
  294. folder_peer->add(file_peer, false);
  295. file_my->set_source(file_peer);
  296. auto path = root_path / "a.txt";
  297. auto path_tmp = root_path / "a.txt.syncspirit-tmp";
  298. write_file(path, "12345");
  299. write_file(path_tmp, "12345");
  300. bfs::last_write_time(path, modified);
  301. auto task = scan_task_t(cluster, folder->get_id(), config);
  302. auto r = task.advance();
  303. CHECK(std::get_if<bool>(&r));
  304. CHECK(*std::get_if<bool>(&r) == true);
  305. unchanged_meta_t unchanged;
  306. incomplete_t incomplete;
  307. for (int i = 0; i < 2; ++i) {
  308. r = task.advance();
  309. std::visit(
  310. [&](auto &&it) {
  311. using T = std::decay_t<decltype(it)>;
  312. if constexpr (std::is_same_v<T, unchanged_meta_t>) {
  313. unchanged = it;
  314. } else if constexpr (std::is_same_v<T, incomplete_t>) {
  315. incomplete = it;
  316. } else {
  317. REQUIRE((0 && "unexpected result"));
  318. }
  319. },
  320. r);
  321. }
  322. CHECK(unchanged.file == file_my);
  323. CHECK(incomplete.file);
  324. CHECK(incomplete.opened_file);
  325. r = task.advance();
  326. CHECK(std::get_if<bool>(&r));
  327. CHECK(*std::get_if<bool>(&r) == false);
  328. }
  329. SECTION("cannot read file error") {
  330. pr_file.set_name("a.txt");
  331. auto path = root_path / "a.txt";
  332. write_file(path, "12345");
  333. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  334. folder_my->add(file, false);
  335. auto task = scan_task_t(cluster, folder->get_id(), config);
  336. auto r = task.advance();
  337. CHECK(std::get_if<bool>(&r));
  338. CHECK(*std::get_if<bool>(&r) == true);
  339. bfs::remove(path);
  340. r = task.advance();
  341. REQUIRE(std::get_if<file_error_t>(&r));
  342. auto err = std::get_if<file_error_t>(&r);
  343. REQUIRE(err->file == file);
  344. REQUIRE(err->ec);
  345. r = task.advance();
  346. CHECK(std::get_if<bool>(&r));
  347. CHECK(*std::get_if<bool>(&r) == false);
  348. }
  349. SECTION("cannot read dir, error") {
  350. pr_file.set_name("some/a.txt");
  351. auto path = root_path / "some" / "a.txt";
  352. auto parent = path.parent_path();
  353. write_file(path, "12345");
  354. sys::error_code ec;
  355. bfs::permissions(parent, bfs::perms::no_perms, ec);
  356. bfs::permissions(path, bfs::perms::owner_read, ec);
  357. if (ec) {
  358. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  359. folder_my->add(file, false);
  360. auto task = scan_task_t(cluster, folder->get_id(), config);
  361. auto r = task.advance();
  362. CHECK(std::get_if<bool>(&r));
  363. CHECK(*std::get_if<bool>(&r) == true);
  364. r = task.advance();
  365. REQUIRE(std::get_if<scan_errors_t>(&r));
  366. auto errs = std::get_if<scan_errors_t>(&r);
  367. REQUIRE(errs);
  368. REQUIRE(errs->size() == 1);
  369. REQUIRE(errs->at(0).path == parent);
  370. REQUIRE(errs->at(0).ec);
  371. r = task.advance();
  372. REQUIRE(std::get_if<bool>(&r));
  373. CHECK(*std::get_if<bool>(&r) == false);
  374. bfs::permissions(parent, bfs::perms::all_all);
  375. }
  376. }
  377. }
  378. SECTION("symlink file") {
  379. auto modified = std::time_t{1642007468};
  380. auto pr_file = proto::FileInfo{};
  381. pr_file.set_name("a.txt");
  382. pr_file.set_sequence(2);
  383. pr_file.set_type(proto::FileInfoType::SYMLINK);
  384. pr_file.set_symlink_target("b.txt");
  385. auto version = pr_file.mutable_version();
  386. auto counter = version->add_counters();
  387. counter->set_id(1);
  388. counter->set_value(peer_device->as_uint());
  389. SECTION("the same symlink exists") {
  390. pr_file.set_modified_s(modified);
  391. auto path = root_path / "a.txt";
  392. auto target = bfs::path("b.txt");
  393. bfs::create_symlink(target, path);
  394. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  395. folder_my->add(file, false);
  396. auto task = scan_task_t(cluster, folder->get_id(), config);
  397. auto r = task.advance();
  398. CHECK(std::get_if<bool>(&r));
  399. CHECK(*std::get_if<bool>(&r) == true);
  400. r = task.advance();
  401. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  402. auto ref = std::get_if<unchanged_meta_t>(&r);
  403. CHECK(ref->file == file);
  404. r = task.advance();
  405. CHECK(std::get_if<bool>(&r));
  406. CHECK(*std::get_if<bool>(&r) == false);
  407. }
  408. SECTION("symlink points to something different") {
  409. pr_file.set_modified_s(modified);
  410. auto path = root_path / "a.txt";
  411. auto target = bfs::path("c.txt");
  412. bfs::create_symlink(target, path);
  413. auto file = file_info_t::create(cluster->next_uuid(), pr_file, folder_my).value();
  414. folder_my->add(file, false);
  415. auto task = scan_task_t(cluster, folder->get_id(), config);
  416. auto r = task.advance();
  417. CHECK(std::get_if<bool>(&r));
  418. CHECK(*std::get_if<bool>(&r) == true);
  419. r = task.advance();
  420. REQUIRE(std::get_if<changed_meta_t>(&r));
  421. auto ref = std::get_if<changed_meta_t>(&r);
  422. CHECK(ref->file == file);
  423. r = task.advance();
  424. CHECK(std::get_if<bool>(&r));
  425. CHECK(*std::get_if<bool>(&r) == false);
  426. }
  427. }
  428. }