053-scan_task.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818
  1. // SPDX-License-Identifier: GPL-3.0-or-later
  2. // SPDX-FileCopyrightText: 2019-2025 Ivan Baidakou
  3. #include "test-utils.h"
  4. #include "fs/scan_task.h"
  5. #include "fs/utils.h"
  6. #include "model/misc/sequencer.h"
  7. using namespace syncspirit;
  8. using namespace syncspirit::test;
  9. using namespace syncspirit::utils;
  10. using namespace syncspirit::model;
  11. using namespace syncspirit::fs;
  12. TEST_CASE("scan_task", "[fs]") {
  13. test::init_logging();
  14. auto root_path = unique_path();
  15. bfs::create_directories(root_path);
  16. path_guard_t path_quard{root_path};
  17. config::fs_config_t config{3600, 10, 1024 * 1024, 100};
  18. auto my_id = device_id_t::from_string("KHQNO2S-5QSILRK-YX4JZZ4-7L77APM-QNVGZJT-EKU7IFI-PNEPBMY-4MXFMQD").value();
  19. auto my_device = device_t::create(my_id, "my-device").value();
  20. auto peer_id = device_id_t::from_string("VUV42CZ-IQD5A37-RPEBPM4-VVQK6E4-6WSKC7B-PVJQHHD-4PZD44V-ENC6WAZ").value();
  21. auto peer_device = device_t::create(peer_id, "peer-device").value();
  22. auto peer2_id = device_id_t::from_string("XBOWTOU-Y7H6RM6-D7WT3UB-7P2DZ5G-R6GNZG6-T5CCG54-SGVF3U5-LBM7RQB").value();
  23. auto peer2_device = device_t::create(peer2_id, "peer2-device").value();
  24. auto cluster = cluster_ptr_t(new cluster_t(my_device, 1));
  25. auto sequencer = make_sequencer(4);
  26. cluster->get_devices().put(my_device);
  27. cluster->get_devices().put(peer_device);
  28. cluster->get_devices().put(peer2_device);
  29. auto db_folder = db::Folder();
  30. db_folder.set_id("some-id");
  31. db_folder.set_label("zzz");
  32. db_folder.set_path(root_path.string());
  33. auto folder = folder_t::create(sequencer->next_uuid(), db_folder).value();
  34. cluster->get_folders().put(folder);
  35. db::FolderInfo db_folder_info;
  36. db_folder_info.set_index_id(1234);
  37. db_folder_info.set_max_sequence(3);
  38. auto folder_my = folder_info_t::create(sequencer->next_uuid(), db_folder_info, my_device, folder).value();
  39. auto folder_peer = folder_info_t::create(sequencer->next_uuid(), db_folder_info, peer_device, folder).value();
  40. auto folder_peer2 = folder_info_t::create(sequencer->next_uuid(), db_folder_info, peer2_device, folder).value();
  41. folder->get_folder_infos().put(folder_my);
  42. folder->get_folder_infos().put(folder_peer);
  43. folder->get_folder_infos().put(folder_peer2);
  44. SECTION("without files"){
  45. #ifndef SYNCSPIRIT_WIN
  46. SECTION("no permissions to read dir => err"){bfs::permissions(root_path, bfs::perms::none);
  47. auto folder_info = folder_info_t::create(sequencer->next_uuid(), db_folder_info, my_device, folder).value();
  48. folder->get_folder_infos().put(folder_info);
  49. auto task = scan_task_t(cluster, folder->get_id(), config);
  50. auto r = task.advance();
  51. CHECK(std::get_if<scan_errors_t>(&r));
  52. auto errs = std::get_if<scan_errors_t>(&r);
  53. REQUIRE(errs->size() == 1);
  54. auto &err = errs->at(0);
  55. CHECK(err.ec);
  56. CHECK(err.path == root_path);
  57. }
  58. #endif
  59. SECTION("no dirs, no files") {
  60. auto task = scan_task_t(cluster, folder->get_id(), config);
  61. auto r = task.advance();
  62. CHECK(std::get_if<bool>(&r));
  63. CHECK(*std::get_if<bool>(&r) == true);
  64. r = task.advance();
  65. CHECK(std::get_if<bool>(&r));
  66. CHECK(*std::get_if<bool>(&r) == false);
  67. }
  68. SECTION("some dirs, no files") {
  69. auto task = scan_task_t(cluster, folder->get_id(), config);
  70. auto dir = root_path / "some-dir";
  71. bfs::create_directories(dir);
  72. auto r = task.advance();
  73. CHECK(std::get_if<bool>(&r));
  74. CHECK(*std::get_if<bool>(&r) == true);
  75. r = task.advance();
  76. auto *uf = std::get_if<unknown_file_t>(&r);
  77. REQUIRE(uf);
  78. CHECK(uf->path.filename() == "some-dir");
  79. CHECK(uf->metadata.size() == 0);
  80. CHECK(uf->metadata.type() == proto::FileInfoType::DIRECTORY);
  81. r = task.advance();
  82. CHECK(std::get_if<bool>(&r));
  83. CHECK(*std::get_if<bool>(&r) == true);
  84. r = task.advance();
  85. CHECK(std::get_if<bool>(&r));
  86. CHECK(*std::get_if<bool>(&r) == false);
  87. }
  88. SECTION("no dirs, unknown files") {
  89. auto task = scan_task_t(cluster, folder->get_id(), config);
  90. write_file(root_path / "some-file", "");
  91. auto r = task.advance();
  92. CHECK(std::get_if<bool>(&r));
  93. CHECK(*std::get_if<bool>(&r) == true);
  94. r = task.advance();
  95. auto *uf = std::get_if<unknown_file_t>(&r);
  96. REQUIRE(uf);
  97. CHECK(uf->path.filename() == "some-file");
  98. CHECK(uf->metadata.size() == 0);
  99. CHECK(uf->metadata.type() == proto::FileInfoType::FILE);
  100. r = task.advance();
  101. REQUIRE(std::get_if<bool>(&r));
  102. CHECK(*std::get_if<bool>(&r) == false);
  103. }
  104. #ifndef SYNCSPIRIT_WIN
  105. SECTION("no dirs, symlink to non-existing target") {
  106. auto task = scan_task_t(cluster, folder->get_id(), config);
  107. auto file_path = root_path / "symlink";
  108. bfs::create_symlink(bfs::path("/some/where"), file_path);
  109. auto r = task.advance();
  110. CHECK(std::get_if<bool>(&r));
  111. CHECK(*std::get_if<bool>(&r) == true);
  112. r = task.advance();
  113. auto *uf = std::get_if<unknown_file_t>(&r);
  114. REQUIRE(uf);
  115. CHECK(uf->path.filename() == "symlink");
  116. CHECK(uf->metadata.size() == 0);
  117. CHECK(uf->metadata.type() == proto::FileInfoType::SYMLINK);
  118. CHECK(uf->metadata.symlink_target() == "/some/where");
  119. r = task.advance();
  120. REQUIRE(std::get_if<bool>(&r));
  121. CHECK(*std::get_if<bool>(&r) == false);
  122. }
  123. #endif
  124. }
  125. SECTION("regular files") {
  126. auto modified = std::int64_t{1642007468};
  127. auto pr_file = proto::FileInfo{};
  128. pr_file.set_name("a.txt");
  129. pr_file.set_sequence(4);
  130. auto version = pr_file.mutable_version();
  131. auto counter = version->add_counters();
  132. counter->set_value(1);
  133. counter->set_id(peer_device->device_id().get_uint());
  134. SECTION("meta is not changed (file)") {
  135. pr_file.set_block_size(5);
  136. pr_file.set_size(5);
  137. pr_file.set_modified_s(modified);
  138. auto path = root_path / "a.txt";
  139. write_file(path, "12345");
  140. bfs::last_write_time(path, from_unix(modified));
  141. auto status = bfs::status(path);
  142. pr_file.set_permissions(static_cast<uint32_t>(status.permissions()));
  143. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  144. REQUIRE(folder_my->add_strict(file));
  145. auto task = scan_task_t(cluster, folder->get_id(), config);
  146. auto r = task.advance();
  147. CHECK(std::get_if<bool>(&r));
  148. CHECK(*std::get_if<bool>(&r) == true);
  149. r = task.advance();
  150. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  151. auto ref = std::get_if<unchanged_meta_t>(&r);
  152. CHECK(ref->file == file);
  153. r = task.advance();
  154. CHECK(std::get_if<bool>(&r));
  155. CHECK(*std::get_if<bool>(&r) == false);
  156. }
  157. SECTION("meta is not changed (dir)") {
  158. pr_file.set_name("a-dir");
  159. pr_file.set_type(proto::FileInfoType::DIRECTORY);
  160. auto dir = root_path / "a-dir";
  161. bfs::create_directories(dir);
  162. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  163. REQUIRE(folder_my->add_strict(file));
  164. auto task = scan_task_t(cluster, folder->get_id(), config);
  165. auto r = task.advance();
  166. CHECK(std::get_if<bool>(&r));
  167. CHECK(*std::get_if<bool>(&r) == true);
  168. r = task.advance();
  169. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  170. auto ref = std::get_if<unchanged_meta_t>(&r);
  171. CHECK(ref->file == file);
  172. r = task.advance();
  173. CHECK(std::get_if<bool>(&r));
  174. CHECK(*std::get_if<bool>(&r) == true);
  175. r = task.advance();
  176. CHECK(std::get_if<bool>(&r));
  177. CHECK(*std::get_if<bool>(&r) == false);
  178. }
  179. SECTION("meta is not changed (dir + file inside)") {
  180. auto pr_dir = pr_file;
  181. pr_dir.set_name("a-dir-2");
  182. pr_dir.set_type(proto::FileInfoType::DIRECTORY);
  183. pr_dir.set_sequence(pr_file.sequence() + 1);
  184. pr_file.set_block_size(5);
  185. pr_file.set_size(5);
  186. pr_file.set_modified_s(modified);
  187. pr_file.set_name("a-dir-2/a.txt");
  188. auto dir = root_path / "a-dir-2";
  189. bfs::create_directories(dir);
  190. auto path = root_path / "a-dir-2" / "a.txt";
  191. write_file(path, "12345");
  192. bfs::last_write_time(path, from_unix(modified));
  193. auto status = bfs::status(path);
  194. pr_file.set_permissions(static_cast<uint32_t>(status.permissions()));
  195. auto info_file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  196. REQUIRE(folder_my->add_strict(info_file));
  197. auto info_dir = file_info_t::create(sequencer->next_uuid(), pr_dir, folder_my).value();
  198. REQUIRE(folder_my->add_strict(info_dir));
  199. auto task = scan_task_t(cluster, folder->get_id(), config);
  200. auto r = task.advance();
  201. CHECK(std::get_if<bool>(&r));
  202. CHECK(*std::get_if<bool>(&r) == true);
  203. r = task.advance();
  204. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  205. auto ref = std::get_if<unchanged_meta_t>(&r);
  206. CHECK(ref->file == info_dir);
  207. r = task.advance();
  208. CHECK(std::get_if<bool>(&r));
  209. CHECK(*std::get_if<bool>(&r) == true);
  210. r = task.advance();
  211. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  212. ref = std::get_if<unchanged_meta_t>(&r);
  213. CHECK(ref->file == info_file);
  214. r = task.advance();
  215. CHECK(std::get_if<bool>(&r));
  216. CHECK(*std::get_if<bool>(&r) == false);
  217. }
  218. SECTION("file has been removed") {
  219. pr_file.set_block_size(5);
  220. pr_file.set_size(5);
  221. pr_file.set_modified_s(modified);
  222. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  223. REQUIRE(folder_my->add_strict(file));
  224. auto task = scan_task_t(cluster, folder->get_id(), config);
  225. auto r = task.advance();
  226. CHECK(std::get_if<bool>(&r));
  227. CHECK(*std::get_if<bool>(&r) == true);
  228. r = task.advance();
  229. REQUIRE(std::get_if<removed_t>(&r));
  230. auto ref = std::get_if<removed_t>(&r);
  231. CHECK(ref->file == file);
  232. r = task.advance();
  233. CHECK(std::get_if<bool>(&r));
  234. CHECK(*std::get_if<bool>(&r) == false);
  235. }
  236. SECTION("dir has been removed") {
  237. pr_file.set_name("a-dir");
  238. pr_file.set_type(proto::FileInfoType::DIRECTORY);
  239. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  240. REQUIRE(folder_my->add_strict(file));
  241. auto task = scan_task_t(cluster, folder->get_id(), config);
  242. auto r = task.advance();
  243. CHECK(std::get_if<bool>(&r));
  244. CHECK(*std::get_if<bool>(&r) == true);
  245. r = task.advance();
  246. REQUIRE(std::get_if<removed_t>(&r));
  247. auto ref = std::get_if<removed_t>(&r);
  248. CHECK(ref->file == file);
  249. r = task.advance();
  250. CHECK(std::get_if<bool>(&r));
  251. CHECK(*std::get_if<bool>(&r) == false);
  252. }
  253. SECTION("removed file does not exist => unchanged meta") {
  254. pr_file.set_deleted(true);
  255. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  256. REQUIRE(folder_my->add_strict(file));
  257. auto task = scan_task_t(cluster, folder->get_id(), config);
  258. auto r = task.advance();
  259. CHECK(std::get_if<bool>(&r));
  260. CHECK(*std::get_if<bool>(&r) == true);
  261. r = task.advance();
  262. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  263. auto ref = std::get_if<unchanged_meta_t>(&r);
  264. CHECK(ref->file == file);
  265. r = task.advance();
  266. CHECK(std::get_if<bool>(&r));
  267. CHECK(*std::get_if<bool>(&r) == false);
  268. }
  269. SECTION("removed dir does not exist => unchanged meta") {
  270. pr_file.set_deleted(true);
  271. pr_file.set_type(proto::FileInfoType::DIRECTORY);
  272. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  273. REQUIRE(folder_my->add_strict(file));
  274. auto task = scan_task_t(cluster, folder->get_id(), config);
  275. auto r = task.advance();
  276. CHECK(std::get_if<bool>(&r));
  277. CHECK(*std::get_if<bool>(&r) == true);
  278. r = task.advance();
  279. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  280. auto ref = std::get_if<unchanged_meta_t>(&r);
  281. CHECK(ref->file == file);
  282. r = task.advance();
  283. CHECK(std::get_if<bool>(&r));
  284. CHECK(*std::get_if<bool>(&r) == false);
  285. }
  286. SECTION("root dir does not exist & deleted file => unchanged meta") {
  287. pr_file.set_deleted(true);
  288. folder->set_path(root_path / "zzz");
  289. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  290. REQUIRE(folder_my->add_strict(file));
  291. auto task = scan_task_t(cluster, folder->get_id(), config);
  292. auto r = task.advance();
  293. REQUIRE(std::get_if<bool>(&r));
  294. CHECK(*std::get_if<bool>(&r) == true);
  295. r = task.advance();
  296. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  297. auto ref = std::get_if<unchanged_meta_t>(&r);
  298. CHECK(ref->file == file);
  299. r = task.advance();
  300. CHECK(std::get_if<bool>(&r));
  301. CHECK(*std::get_if<bool>(&r) == false);
  302. }
  303. SECTION("meta is changed") {
  304. auto task = scan_task_ptr_t{};
  305. auto file = file_info_ptr_t{};
  306. auto r = scan_result_t{};
  307. SECTION("file size differs") {
  308. pr_file.set_block_size(5);
  309. pr_file.set_size(6);
  310. pr_file.set_modified_s(modified);
  311. auto path = root_path / "a.txt";
  312. write_file(path, "12345");
  313. bfs::last_write_time(path, from_unix(modified));
  314. auto status = bfs::status(path);
  315. pr_file.set_permissions(static_cast<uint32_t>(status.permissions()));
  316. file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  317. REQUIRE(folder_my->add_strict(file));
  318. task = new scan_task_t(cluster, folder->get_id(), config);
  319. r = task->advance();
  320. CHECK(std::get_if<bool>(&r));
  321. CHECK(*std::get_if<bool>(&r) == true);
  322. r = task->advance();
  323. REQUIRE(std::get_if<changed_meta_t>(&r));
  324. auto ref = std::get_if<changed_meta_t>(&r);
  325. CHECK(ref->file == file);
  326. CHECK(ref->metadata.size() == 5);
  327. CHECK(ref->metadata.modified_s() == modified);
  328. }
  329. SECTION("modification time differs") {
  330. pr_file.set_block_size(5);
  331. pr_file.set_size(5);
  332. pr_file.set_modified_s(modified + 1);
  333. auto path = root_path / "a.txt";
  334. write_file(path, "12345");
  335. bfs::last_write_time(path, from_unix(modified));
  336. auto status = bfs::status(path);
  337. pr_file.set_permissions(static_cast<uint32_t>(status.permissions()));
  338. file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  339. REQUIRE(folder_my->add_strict(file));
  340. task = new scan_task_t(cluster, folder->get_id(), config);
  341. r = task->advance();
  342. CHECK(std::get_if<bool>(&r));
  343. CHECK(*std::get_if<bool>(&r) == true);
  344. r = task->advance();
  345. REQUIRE(std::get_if<changed_meta_t>(&r));
  346. auto ref = std::get_if<changed_meta_t>(&r);
  347. CHECK(ref->file == file);
  348. CHECK(ref->metadata.size() == 5);
  349. CHECK(ref->metadata.modified_s() == modified);
  350. }
  351. SECTION("permissions differs") {
  352. pr_file.set_block_size(5);
  353. pr_file.set_size(5);
  354. pr_file.set_modified_s(modified);
  355. pr_file.set_permissions(static_cast<uint32_t>(-1));
  356. auto path = root_path / "a.txt";
  357. write_file(path, "12345");
  358. bfs::last_write_time(path, from_unix(modified));
  359. file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  360. REQUIRE(folder_my->add_strict(file));
  361. task = new scan_task_t(cluster, folder->get_id(), config);
  362. r = task->advance();
  363. CHECK(std::get_if<bool>(&r));
  364. CHECK(*std::get_if<bool>(&r) == true);
  365. r = task->advance();
  366. REQUIRE(std::get_if<changed_meta_t>(&r));
  367. auto ref = std::get_if<changed_meta_t>(&r);
  368. CHECK(ref->file == file);
  369. }
  370. r = task->advance();
  371. CHECK(std::get_if<bool>(&r));
  372. CHECK(*std::get_if<bool>(&r) == false);
  373. }
  374. SECTION("tmp") {
  375. pr_file.set_block_size(5);
  376. pr_file.set_size(5);
  377. pr_file.set_modified_s(modified);
  378. auto path = root_path / "a.txt.syncspirit-tmp";
  379. SECTION("size match -> ok, will recalc") {
  380. write_file(path, "12345");
  381. auto file_peer = file_info_t::create(sequencer->next_uuid(), pr_file, folder_peer).value();
  382. REQUIRE(folder_peer->add_strict(file_peer));
  383. auto task = scan_task_t(cluster, folder->get_id(), config);
  384. auto r = task.advance();
  385. CHECK(std::get_if<bool>(&r));
  386. CHECK(*std::get_if<bool>(&r) == true);
  387. r = task.advance();
  388. REQUIRE(std::get_if<incomplete_t>(&r));
  389. auto ref = std::get_if<incomplete_t>(&r);
  390. CHECK(ref->file);
  391. CHECK(ref->opened_file);
  392. r = task.advance();
  393. CHECK(std::get_if<bool>(&r));
  394. CHECK(*std::get_if<bool>(&r) == false);
  395. }
  396. SECTION("size mismatch -> remove & ignore") {
  397. write_file(path, "123456");
  398. auto file_peer = file_info_t::create(sequencer->next_uuid(), pr_file, folder_peer).value();
  399. REQUIRE(folder_peer->add_strict(file_peer));
  400. auto task = scan_task_t(cluster, folder->get_id(), config);
  401. auto r = task.advance();
  402. CHECK(std::get_if<bool>(&r));
  403. CHECK(*std::get_if<bool>(&r) == true);
  404. r = task.advance();
  405. CHECK(std::get_if<incomplete_removed_t>(&r));
  406. CHECK(std::get_if<incomplete_removed_t>(&r)->file == file_peer);
  407. r = task.advance();
  408. CHECK(std::get_if<bool>(&r));
  409. CHECK(*std::get_if<bool>(&r) == false);
  410. CHECK(!bfs::exists(path));
  411. }
  412. SECTION("size mismatch for global source -> remove & ignore") {
  413. write_file(path, "123456");
  414. auto file_peer = file_info_t::create(sequencer->next_uuid(), pr_file, folder_peer).value();
  415. REQUIRE(folder_peer->add_strict(file_peer));
  416. pr_file.set_size(file_peer->get_size() + 10);
  417. auto c2 = version->add_counters();
  418. c2->set_id(peer2_device->device_id().get_uint());
  419. c2->set_value(2);
  420. auto file_peer2 = file_info_t::create(sequencer->next_uuid(), pr_file, folder_peer2).value();
  421. REQUIRE(folder_peer2->add_strict(file_peer2));
  422. auto task = scan_task_t(cluster, folder->get_id(), config);
  423. auto r = task.advance();
  424. CHECK(std::get_if<bool>(&r));
  425. CHECK(*std::get_if<bool>(&r) == true);
  426. r = task.advance();
  427. CHECK(std::get_if<incomplete_removed_t>(&r));
  428. CHECK(std::get_if<incomplete_removed_t>(&r)->file == file_peer2);
  429. r = task.advance();
  430. CHECK(std::get_if<bool>(&r));
  431. CHECK(*std::get_if<bool>(&r) == false);
  432. CHECK(!bfs::exists(path));
  433. }
  434. SECTION("no source -> remove") {
  435. write_file(path, "123456");
  436. auto task = scan_task_t(cluster, folder->get_id(), config);
  437. auto r = task.advance();
  438. CHECK(std::get_if<bool>(&r));
  439. CHECK(*std::get_if<bool>(&r) == true);
  440. r = task.advance();
  441. CHECK(std::get_if<orphaned_removed_t>(&r));
  442. CHECK(std::get_if<orphaned_removed_t>(&r)->path == path);
  443. r = task.advance();
  444. CHECK(std::get_if<bool>(&r));
  445. CHECK(*std::get_if<bool>(&r) == false);
  446. CHECK(!bfs::exists(path));
  447. }
  448. }
  449. SECTION("tmp & non-tmp: both are returned") {
  450. pr_file.set_block_size(5);
  451. pr_file.set_size(5);
  452. pr_file.set_modified_s(modified);
  453. auto path = root_path / "a.txt";
  454. auto path_tmp = root_path / "a.txt.syncspirit-tmp";
  455. write_file(path, "12345");
  456. write_file(path_tmp, "12345");
  457. bfs::last_write_time(path, from_unix(modified));
  458. auto status = bfs::status(path);
  459. pr_file.set_permissions(static_cast<uint32_t>(status.permissions()));
  460. auto file_my = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  461. counter->set_id(10);
  462. auto file_peer = file_info_t::create(sequencer->next_uuid(), pr_file, folder_peer).value();
  463. REQUIRE(folder_my->add_strict(file_my));
  464. REQUIRE(folder_peer->add_strict(file_peer));
  465. auto task = scan_task_t(cluster, folder->get_id(), config);
  466. auto r = task.advance();
  467. CHECK(std::get_if<bool>(&r));
  468. CHECK(*std::get_if<bool>(&r) == true);
  469. unchanged_meta_t unchanged;
  470. incomplete_t incomplete;
  471. for (int i = 0; i < 2; ++i) {
  472. r = task.advance();
  473. std::visit(
  474. [&](auto &&it) {
  475. using T = std::decay_t<decltype(it)>;
  476. if constexpr (std::is_same_v<T, unchanged_meta_t>) {
  477. unchanged = it;
  478. } else if constexpr (std::is_same_v<T, incomplete_t>) {
  479. incomplete = it;
  480. } else {
  481. REQUIRE((0 && "unexpected result"));
  482. }
  483. },
  484. r);
  485. }
  486. CHECK(unchanged.file == file_my);
  487. CHECK(incomplete.file);
  488. CHECK(incomplete.opened_file);
  489. r = task.advance();
  490. CHECK(std::get_if<bool>(&r));
  491. CHECK(*std::get_if<bool>(&r) == false);
  492. }
  493. SECTION("cannot read file error") {
  494. pr_file.set_name("a.txt");
  495. auto path = root_path / "a.txt";
  496. write_file(path, "12345");
  497. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  498. REQUIRE(folder_my->add_strict(file));
  499. auto task = scan_task_t(cluster, folder->get_id(), config);
  500. auto r = task.advance();
  501. CHECK(std::get_if<bool>(&r));
  502. CHECK(*std::get_if<bool>(&r) == true);
  503. bfs::remove(path);
  504. r = task.advance();
  505. REQUIRE(std::get_if<file_error_t>(&r));
  506. auto err = std::get_if<file_error_t>(&r);
  507. REQUIRE(err->path == path);
  508. REQUIRE(err->ec);
  509. r = task.advance();
  510. CHECK(std::get_if<bool>(&r));
  511. CHECK(*std::get_if<bool>(&r) == false);
  512. }
  513. SECTION("cannot read dir, error") {
  514. pr_file.set_name("some/a.txt");
  515. auto path = root_path / "some" / "a.txt";
  516. auto parent = path.parent_path();
  517. write_file(path, "12345");
  518. sys::error_code ec;
  519. bfs::permissions(parent, bfs::perms::none, ec);
  520. bfs::permissions(path, bfs::perms::owner_read, ec);
  521. if (ec) {
  522. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  523. REQUIRE(folder_my->add_strict(file));
  524. auto task = scan_task_t(cluster, folder->get_id(), config);
  525. auto r = task.advance();
  526. CHECK(std::get_if<bool>(&r));
  527. CHECK(*std::get_if<bool>(&r) == true);
  528. r = task.advance();
  529. auto *uf = std::get_if<unknown_file_t>(&r);
  530. REQUIRE(uf);
  531. CHECK(uf->path.filename() == "some");
  532. CHECK(uf->metadata.size() == 0);
  533. CHECK(uf->metadata.type() == proto::FileInfoType::DIRECTORY);
  534. r = task.advance();
  535. REQUIRE(std::get_if<scan_errors_t>(&r));
  536. auto errs = std::get_if<scan_errors_t>(&r);
  537. REQUIRE(errs);
  538. REQUIRE(errs->size() == 1);
  539. REQUIRE(errs->at(0).path == parent);
  540. REQUIRE(errs->at(0).ec);
  541. r = task.advance();
  542. REQUIRE(std::get_if<bool>(&r));
  543. CHECK(*std::get_if<bool>(&r) == false);
  544. bfs::permissions(parent, bfs::perms::all);
  545. }
  546. }
  547. }
  548. #ifndef SYNCSPIRIT_WIN
  549. SECTION("symlink file") {
  550. auto modified = std::time_t{1642007468};
  551. auto pr_file = proto::FileInfo{};
  552. pr_file.set_name("a.txt");
  553. pr_file.set_sequence(4);
  554. pr_file.set_type(proto::FileInfoType::SYMLINK);
  555. pr_file.set_symlink_target("b.txt");
  556. auto version = pr_file.mutable_version();
  557. auto counter = version->add_counters();
  558. counter->set_id(1);
  559. counter->set_value(peer_device->device_id().get_uint());
  560. SECTION("symlink does not exists") {
  561. pr_file.set_modified_s(modified);
  562. auto path = root_path / "a.txt";
  563. auto target = bfs::path("b.txt");
  564. bfs::create_symlink(target, path);
  565. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  566. REQUIRE(folder_my->add_strict(file));
  567. auto task = scan_task_t(cluster, folder->get_id(), config);
  568. auto r = task.advance();
  569. CHECK(std::get_if<bool>(&r));
  570. CHECK(*std::get_if<bool>(&r) == true);
  571. r = task.advance();
  572. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  573. auto ref = std::get_if<unchanged_meta_t>(&r);
  574. CHECK(ref->file == file);
  575. r = task.advance();
  576. CHECK(std::get_if<bool>(&r));
  577. CHECK(*std::get_if<bool>(&r) == false);
  578. }
  579. SECTION("symlink does exists") {
  580. pr_file.set_modified_s(modified);
  581. pr_file.set_symlink_target("b");
  582. auto path = root_path / "a.txt";
  583. auto target = bfs::path("b");
  584. bfs::create_symlink(target, path);
  585. bfs::create_directories(root_path / target);
  586. auto pr_dir = proto::FileInfo{};
  587. pr_dir.set_name("b");
  588. pr_dir.set_sequence(pr_file.sequence() + 1);
  589. pr_dir.set_type(proto::FileInfoType::DIRECTORY);
  590. *pr_dir.mutable_version() = pr_file.version();
  591. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  592. auto dir = file_info_t::create(sequencer->next_uuid(), pr_dir, folder_my).value();
  593. REQUIRE(folder_my->add_strict(file));
  594. REQUIRE(folder_my->add_strict(dir));
  595. auto task = scan_task_t(cluster, folder->get_id(), config);
  596. auto r = task.advance();
  597. CHECK(std::get_if<bool>(&r));
  598. CHECK(*std::get_if<bool>(&r) == true);
  599. r = task.advance();
  600. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  601. auto ref = std::get_if<unchanged_meta_t>(&r);
  602. CHECK(((ref->file == dir) || (ref->file == file)));
  603. r = task.advance();
  604. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  605. ref = std::get_if<unchanged_meta_t>(&r);
  606. CHECK(((ref->file == dir) || (ref->file == file)));
  607. r = task.advance();
  608. CHECK(std::get_if<bool>(&r));
  609. CHECK(*std::get_if<bool>(&r) == true);
  610. r = task.advance();
  611. CHECK(std::get_if<bool>(&r));
  612. CHECK(*std::get_if<bool>(&r) == false);
  613. }
  614. SECTION("symlink to root") {
  615. pr_file.set_modified_s(modified);
  616. pr_file.set_symlink_target("/");
  617. auto path = root_path / "a.txt";
  618. auto target = bfs::path("/");
  619. bfs::create_symlink(target, path);
  620. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  621. REQUIRE(folder_my->add_strict(file));
  622. auto task = scan_task_t(cluster, folder->get_id(), config);
  623. auto r = task.advance();
  624. CHECK(std::get_if<bool>(&r));
  625. CHECK(*std::get_if<bool>(&r) == true);
  626. r = task.advance();
  627. REQUIRE(std::get_if<unchanged_meta_t>(&r));
  628. auto ref = std::get_if<unchanged_meta_t>(&r);
  629. CHECK(ref->file == file);
  630. r = task.advance();
  631. CHECK(std::get_if<bool>(&r));
  632. CHECK(*std::get_if<bool>(&r) == false);
  633. }
  634. SECTION("symlink points to something different") {
  635. pr_file.set_modified_s(modified);
  636. auto path = root_path / "a.txt";
  637. auto target = bfs::path("c.txt");
  638. bfs::create_symlink(target, path);
  639. auto file = file_info_t::create(sequencer->next_uuid(), pr_file, folder_my).value();
  640. REQUIRE(folder_my->add_strict(file));
  641. auto task = scan_task_t(cluster, folder->get_id(), config);
  642. auto r = task.advance();
  643. CHECK(std::get_if<bool>(&r));
  644. CHECK(*std::get_if<bool>(&r) == true);
  645. r = task.advance();
  646. REQUIRE(std::get_if<changed_meta_t>(&r));
  647. auto ref = std::get_if<changed_meta_t>(&r);
  648. CHECK(ref->file == file);
  649. r = task.advance();
  650. CHECK(std::get_if<bool>(&r));
  651. CHECK(*std::get_if<bool>(&r) == false);
  652. }
  653. }
  654. #endif
  655. }