1 /*
2 
3 Copyright (c) 2012, Arvid Norberg
4 All rights reserved.
5 
6 Redistribution and use in source and binary forms, with or without
7 modification, are permitted provided that the following conditions
8 are met:
9 
10     * Redistributions of source code must retain the above copyright
11       notice, this list of conditions and the following disclaimer.
12     * Redistributions in binary form must reproduce the above copyright
13       notice, this list of conditions and the following disclaimer in
14       the documentation and/or other materials provided with the distribution.
15     * Neither the name of the author nor the names of its
16       contributors may be used to endorse or promote products derived
17       from this software without specific prior written permission.
18 
19 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
23 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 POSSIBILITY OF SUCH DAMAGE.
30 
31 */
32 
33 #include "libtorrent/file.hpp"
34 #include "libtorrent/aux_/path.hpp"
35 #include "libtorrent/aux_/numeric_cast.hpp"
36 #include "libtorrent/string_view.hpp"
37 #include "test.hpp"
38 #include <vector>
39 #include <set>
40 #include <thread>
41 #include <iostream>
42 
43 using namespace lt;
44 
45 namespace {
46 
touch_file(std::string const & filename,int size)47 int touch_file(std::string const& filename, int size)
48 {
49 	using namespace lt;
50 
51 	std::vector<char> v;
52 	v.resize(aux::numeric_cast<std::size_t>(size));
53 	for (int i = 0; i < size; ++i)
54 		v[std::size_t(i)] = char(i & 255);
55 
56 	file f;
57 	error_code ec;
58 	if (!f.open(filename, open_mode::write_only, ec)) return -1;
59 	TEST_EQUAL(ec, error_code());
60 	if (ec) return -1;
61 	iovec_t b = {v};
62 	std::int64_t written = f.writev(0, b, ec);
63 	if (written != int(v.size())) return -3;
64 	if (ec) return -3;
65 	TEST_EQUAL(ec, error_code());
66 	return 0;
67 }
68 
69 } // anonymous namespace
70 
TORRENT_TEST(create_directory)71 TORRENT_TEST(create_directory)
72 {
73 	error_code ec;
74 	create_directory("__foobar__", ec);
75 	if (ec) std::printf("ERROR: create_directory: (%d) %s\n"
76 		, ec.value(), ec.message().c_str());
77 	TEST_CHECK(!ec);
78 
79 	file_status st;
80 	stat_file("__foobar__", &st, ec);
81 	if (ec) std::printf("ERROR: stat_file: (%d) %s\n"
82 		, ec.value(), ec.message().c_str());
83 	TEST_CHECK(!ec);
84 
85 	TEST_CHECK(st.mode & file_status::directory);
86 
87 	remove("__foobar__", ec);
88 	if (ec) std::printf("ERROR: remove: (%d) %s\n"
89 		, ec.value(), ec.message().c_str());
90 	TEST_CHECK(!ec);
91 }
92 
TORRENT_TEST(file_status)93 TORRENT_TEST(file_status)
94 {
95 	error_code ec;
96 
97 	// test that the modification timestamps
98 	touch_file("__test_timestamp__", 10);
99 
100 	file_status st1;
101 	stat_file("__test_timestamp__", &st1, ec);
102 	TEST_CHECK(!ec);
103 
104 	// sleep for 3 seconds and then make sure the difference in timestamp is
105 	// between 2-4 seconds after touching it again
106 	std::this_thread::sleep_for(lt::milliseconds(3000));
107 
108 	touch_file("__test_timestamp__", 10);
109 
110 	file_status st2;
111 	stat_file("__test_timestamp__", &st2, ec);
112 	TEST_CHECK(!ec);
113 
114 	int diff = int(st2.mtime - st1.mtime);
115 	std::printf("timestamp difference: %d seconds. expected approx. 3 seconds\n"
116 		, diff);
117 
118 	TEST_CHECK(diff >= 2 && diff <= 4);
119 }
120 
TORRENT_TEST(directory)121 TORRENT_TEST(directory)
122 {
123 	error_code ec;
124 
125 	create_directory("file_test_dir", ec);
126 	if (ec) std::printf("create_directory: %s\n", ec.message().c_str());
127 	TEST_CHECK(!ec);
128 
129 	std::string cwd = current_working_directory();
130 
131 	touch_file(combine_path("file_test_dir", "abc"), 10);
132 	touch_file(combine_path("file_test_dir", "def"), 100);
133 	touch_file(combine_path("file_test_dir", "ghi"), 1000);
134 
135 	std::set<std::string> files;
136 	for (directory i("file_test_dir", ec); !i.done(); i.next(ec))
137 	{
138 		std::string f = i.file();
139 		TEST_CHECK(files.count(f) == 0);
140 		files.insert(f);
141 		std::printf(" %s\n", f.c_str());
142 	}
143 
144 	TEST_CHECK(files.count("abc") == 1);
145 	TEST_CHECK(files.count("def") == 1);
146 	TEST_CHECK(files.count("ghi") == 1);
147 	TEST_CHECK(files.count("..") == 1);
148 	TEST_CHECK(files.count(".") == 1);
149 	files.clear();
150 
151 	recursive_copy("file_test_dir", "file_test_dir2", ec);
152 
153 	for (directory i("file_test_dir2", ec); !i.done(); i.next(ec))
154 	{
155 		std::string f = i.file();
156 		TEST_CHECK(files.count(f) == 0);
157 		files.insert(f);
158 		std::printf(" %s\n", f.c_str());
159 	}
160 
161 	remove_all("file_test_dir", ec);
162 	if (ec) std::printf("remove_all: %s\n", ec.message().c_str());
163 	remove_all("file_test_dir2", ec);
164 	if (ec) std::printf("remove_all: %s\n", ec.message().c_str());
165 }
166 
167 // test path functions
TORRENT_TEST(paths)168 TORRENT_TEST(paths)
169 {
170 	TEST_EQUAL(combine_path("test1/", "test2"), "test1/test2");
171 	TEST_EQUAL(combine_path("test1", "."), "test1");
172 	TEST_EQUAL(combine_path(".", "test1"), "test1");
173 #ifdef TORRENT_WINDOWS
174 	TEST_EQUAL(combine_path("test1\\", "test2"), "test1\\test2");
175 	TEST_EQUAL(combine_path("test1", "test2"), "test1\\test2");
176 #else
177 	TEST_EQUAL(combine_path("test1", "test2"), "test1/test2");
178 #endif
179 
180 	TEST_EQUAL(extension("blah"), "");
181 	TEST_EQUAL(extension("blah.exe"), ".exe");
182 	TEST_EQUAL(extension("blah.foo.bar"), ".bar");
183 	TEST_EQUAL(extension("blah.foo."), ".");
184 	TEST_EQUAL(extension("blah.foo/bar"), "");
185 
186 	TEST_EQUAL(remove_extension("blah"), "blah");
187 	TEST_EQUAL(remove_extension("blah.exe"), "blah");
188 	TEST_EQUAL(remove_extension("blah.foo.bar"), "blah.foo");
189 	TEST_EQUAL(remove_extension("blah.foo."), "blah.foo");
190 
191 #ifdef TORRENT_WINDOWS
192 	TEST_EQUAL(is_root_path("c:\\blah"), false);
193 	TEST_EQUAL(is_root_path("c:\\"), true);
194 	TEST_EQUAL(is_root_path("\\\\"), true);
195 	TEST_EQUAL(is_root_path("\\\\foobar"), true);
196 	TEST_EQUAL(is_root_path("\\\\foobar\\"), true);
197 	TEST_EQUAL(is_root_path("\\\\foobar/"), true);
198 	TEST_EQUAL(is_root_path("\\\\foo/bar"), false);
199 	TEST_EQUAL(is_root_path("\\\\foo\\bar\\"), false);
200 #else
201 	TEST_EQUAL(is_root_path("/blah"), false);
202 	TEST_EQUAL(is_root_path("/"), true);
203 #endif
204 
205 #ifdef TORRENT_WINDOWS
206 	TEST_CHECK(compare_path("c:\\blah\\", "c:\\blah"));
207 	TEST_CHECK(compare_path("c:\\blah", "c:\\blah"));
208 	TEST_CHECK(compare_path("c:\\blah/", "c:\\blah"));
209 	TEST_CHECK(compare_path("c:\\blah", "c:\\blah\\"));
210 	TEST_CHECK(compare_path("c:\\blah", "c:\\blah"));
211 	TEST_CHECK(compare_path("c:\\blah", "c:\\blah/"));
212 
213 	TEST_CHECK(!compare_path("c:\\bla", "c:\\blah/"));
214 	TEST_CHECK(!compare_path("c:\\bla", "c:\\blah"));
215 	TEST_CHECK(!compare_path("c:\\blah", "c:\\bla"));
216 	TEST_CHECK(!compare_path("c:\\blah\\sdf", "c:\\blah"));
217 #else
218 	TEST_CHECK(compare_path("/blah", "/blah"));
219 	TEST_CHECK(compare_path("/blah/", "/blah"));
220 	TEST_CHECK(compare_path("/blah", "/blah"));
221 	TEST_CHECK(compare_path("/blah", "/blah/"));
222 
223 	TEST_CHECK(!compare_path("/bla", "/blah/"));
224 	TEST_CHECK(!compare_path("/bla", "/blah"));
225 	TEST_CHECK(!compare_path("/blah", "/bla"));
226 	TEST_CHECK(!compare_path("/blah/sdf", "/blah"));
227 #endif
228 
229 	// if has_parent_path() returns false
230 	// parent_path() should return the empty string
231 	TEST_EQUAL(parent_path("blah"), "");
232 	TEST_EQUAL(has_parent_path("blah"), false);
233 	TEST_EQUAL(parent_path("/blah/foo/bar"), "/blah/foo/");
234 	TEST_EQUAL(has_parent_path("/blah/foo/bar"), true);
235 	TEST_EQUAL(parent_path("/blah/foo/bar/"), "/blah/foo/");
236 	TEST_EQUAL(has_parent_path("/blah/foo/bar/"), true);
237 	TEST_EQUAL(parent_path("/a"), "/");
238 	TEST_EQUAL(has_parent_path("/a"), true);
239 	TEST_EQUAL(parent_path("/"), "");
240 	TEST_EQUAL(has_parent_path("/"), false);
241 	TEST_EQUAL(parent_path(""), "");
242 	TEST_EQUAL(has_parent_path(""), false);
243 #ifdef TORRENT_WINDOWS
244 	TEST_EQUAL(parent_path("\\\\"), "");
245 	TEST_EQUAL(has_parent_path("\\\\"), false);
246 	TEST_EQUAL(parent_path("c:\\"), "");
247 	TEST_EQUAL(has_parent_path("c:\\"), false);
248 	TEST_EQUAL(parent_path("c:\\a"), "c:\\");
249 	TEST_EQUAL(has_parent_path("c:\\a"), true);
250 	TEST_EQUAL(has_parent_path("\\\\a"), false);
251 	TEST_EQUAL(has_parent_path("\\\\foobar/"), false);
252 	TEST_EQUAL(has_parent_path("\\\\foobar\\"), false);
253 	TEST_EQUAL(has_parent_path("\\\\foo/bar\\"), true);
254 #endif
255 
256 #ifdef TORRENT_WINDOWS
257 	TEST_EQUAL(is_complete("c:\\"), true);
258 	TEST_EQUAL(is_complete("c:\\foo\\bar"), true);
259 	TEST_EQUAL(is_complete("\\\\foo\\bar"), true);
260 	TEST_EQUAL(is_complete("foo/bar"), false);
261 	TEST_EQUAL(is_complete("\\\\"), true);
262 #else
263 	TEST_EQUAL(is_complete("/foo/bar"), true);
264 	TEST_EQUAL(is_complete("foo/bar"), false);
265 	TEST_EQUAL(is_complete("/"), true);
266 	TEST_EQUAL(is_complete(""), false);
267 #endif
268 
269 	TEST_EQUAL(complete("."), current_working_directory());
270 
271 #ifdef TORRENT_WINDOWS
272 	TEST_EQUAL(complete(".\\foobar"), current_working_directory() + "\\foobar");
273 #else
274 	TEST_EQUAL(complete("./foobar"), current_working_directory() + "/foobar");
275 #endif
276 }
277 
TORRENT_TEST(filename)278 TORRENT_TEST(filename)
279 {
280 #ifdef TORRENT_WINDOWS
281 	TEST_EQUAL(filename("blah"), "blah");
282 	TEST_EQUAL(filename("\\blah\\foo\\bar"), "bar");
283 	TEST_EQUAL(filename("\\blah\\foo\\bar\\"), "bar");
284 	TEST_EQUAL(filename("blah\\"), "blah");
285 #endif
286 	TEST_EQUAL(filename("blah"), "blah");
287 	TEST_EQUAL(filename("/blah/foo/bar"), "bar");
288 	TEST_EQUAL(filename("/blah/foo/bar/"), "bar");
289 	TEST_EQUAL(filename("blah/"), "blah");
290 }
291 
TORRENT_TEST(split_path)292 TORRENT_TEST(split_path)
293 {
294 	using r = std::pair<string_view, string_view>;
295 
296 #ifdef TORRENT_WINDOWS
297 	TEST_CHECK(lsplit_path("\\b\\c\\d") == r("b", "c\\d"));
298 	TEST_CHECK(lsplit_path("a\\b\\c\\d") == r("a", "b\\c\\d"));
299 	TEST_CHECK(lsplit_path("a") == r("a", ""));
300 	TEST_CHECK(lsplit_path("") == r("", ""));
301 
302 	TEST_CHECK(lsplit_path("a\\b/c\\d") == r("a", "b/c\\d"));
303 	TEST_CHECK(lsplit_path("a/b\\c\\d") == r("a", "b\\c\\d"));
304 
305 	TEST_CHECK(rsplit_path("a\\b\\c\\d\\") == r("a\\b\\c", "d"));
306 	TEST_CHECK(rsplit_path("\\a\\b\\c\\d") == r("\\a\\b\\c", "d"));
307 	TEST_CHECK(rsplit_path("\\a") == r("", "a"));
308 	TEST_CHECK(rsplit_path("a") == r("", "a"));
309 	TEST_CHECK(rsplit_path("") == r("", ""));
310 
311 	TEST_CHECK(rsplit_path("a\\b/c\\d\\") == r("a\\b/c", "d"));
312 	TEST_CHECK(rsplit_path("a\\b\\c/d\\") == r("a\\b\\c", "d"));
313 #endif
314 	TEST_CHECK(lsplit_path("/b/c/d") == r("b", "c/d"));
315 	TEST_CHECK(lsplit_path("a/b/c/d") == r("a", "b/c/d"));
316 	TEST_CHECK(lsplit_path("a") == r("a", ""));
317 	TEST_CHECK(lsplit_path("") == r("", ""));
318 
319 	TEST_CHECK(rsplit_path("a/b/c/d/") == r("a/b/c", "d"));
320 	TEST_CHECK(rsplit_path("/a/b/c/d") == r("/a/b/c", "d"));
321 	TEST_CHECK(rsplit_path("/a") == r("", "a"));
322 	TEST_CHECK(rsplit_path("a") == r("", "a"));
323 	TEST_CHECK(rsplit_path("") == r("", ""));
324 }
325 
TORRENT_TEST(split_path_pos)326 TORRENT_TEST(split_path_pos)
327 {
328 	using r = std::pair<string_view, string_view>;
329 
330 #ifdef TORRENT_WINDOWS
331 	TEST_CHECK(lsplit_path("\\b\\c\\d", 0) == r("b", "c\\d"));
332 	TEST_CHECK(lsplit_path("\\b\\c\\d", 1) == r("b", "c\\d"));
333 	TEST_CHECK(lsplit_path("\\b\\c\\d", 2) == r("b", "c\\d"));
334 	TEST_CHECK(lsplit_path("\\b\\c\\d", 3) == r("b\\c", "d"));
335 	TEST_CHECK(lsplit_path("\\b\\c\\d", 4) == r("b\\c", "d"));
336 	TEST_CHECK(lsplit_path("\\b\\c\\d", 5) == r("b\\c\\d", ""));
337 	TEST_CHECK(lsplit_path("\\b\\c\\d", 6) == r("b\\c\\d", ""));
338 
339 	TEST_CHECK(lsplit_path("b\\c\\d", 0) == r("b", "c\\d"));
340 	TEST_CHECK(lsplit_path("b\\c\\d", 1) == r("b", "c\\d"));
341 	TEST_CHECK(lsplit_path("b\\c\\d", 2) == r("b\\c", "d"));
342 	TEST_CHECK(lsplit_path("b\\c\\d", 3) == r("b\\c", "d"));
343 	TEST_CHECK(lsplit_path("b\\c\\d", 4) == r("b\\c\\d", ""));
344 	TEST_CHECK(lsplit_path("b\\c\\d", 5) == r("b\\c\\d", ""));
345 #endif
346 	TEST_CHECK(lsplit_path("/b/c/d", 0) == r("b", "c/d"));
347 	TEST_CHECK(lsplit_path("/b/c/d", 1) == r("b", "c/d"));
348 	TEST_CHECK(lsplit_path("/b/c/d", 2) == r("b", "c/d"));
349 	TEST_CHECK(lsplit_path("/b/c/d", 3) == r("b/c", "d"));
350 	TEST_CHECK(lsplit_path("/b/c/d", 4) == r("b/c", "d"));
351 	TEST_CHECK(lsplit_path("/b/c/d", 5) == r("b/c/d", ""));
352 	TEST_CHECK(lsplit_path("/b/c/d", 6) == r("b/c/d", ""));
353 
354 	TEST_CHECK(lsplit_path("b/c/d", 0) == r("b", "c/d"));
355 	TEST_CHECK(lsplit_path("b/c/d", 1) == r("b", "c/d"));
356 	TEST_CHECK(lsplit_path("b/c/d", 2) == r("b/c", "d"));
357 	TEST_CHECK(lsplit_path("b/c/d", 3) == r("b/c", "d"));
358 	TEST_CHECK(lsplit_path("b/c/d", 4) == r("b/c/d", ""));
359 	TEST_CHECK(lsplit_path("b/c/d", 5) == r("b/c/d", ""));
360 }
361 
362 // file class
TORRENT_TEST(file)363 TORRENT_TEST(file)
364 {
365 	error_code ec;
366 	file f;
367 	std::string test_file_name = "test_file";
368 	TEST_CHECK(f.open(test_file_name, open_mode::read_write, ec));
369 	if (ec)
370 		std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
371 	TEST_EQUAL(ec, error_code());
372 	if (ec) std::printf("%s\n", ec.message().c_str());
373 	char test[] = "test";
374 	int const test_word_size = int(sizeof(test)) - 1;
375 	iovec_t b = {test, test_word_size};
376 	TEST_EQUAL(f.writev(0, b, ec), test_word_size);
377 	if (ec)
378 		std::printf("writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
379 	TEST_CHECK(!ec);
380 	char test_buf[test_word_size + 1] = {0};
381 	b = { test_buf, test_word_size };
382 	TEST_EQUAL(f.readv(0, b, ec), test_word_size);
383 	if (ec)
384 		std::printf("readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
385 	TEST_EQUAL(ec, error_code());
386 	TEST_CHECK(test_buf == "test"_sv);
387 	f.close();
388 
389 	TEST_CHECK(f.open(test_file_name, open_mode::read_only, ec));
390 	if (ec)
391 		std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
392 	TEST_EQUAL(ec, error_code());
393 	if (ec) std::printf("%s\n", ec.message().c_str());
394 
395 	char test_buf2[test_word_size + 1] = {0};
396 	std::memset(test_buf, 0, sizeof(test_buf));
397 	iovec_t two_buffers[2] {
398 			{test_buf, test_word_size},
399 			{test_buf2, test_word_size}
400 	};
401 
402 	TEST_EQUAL(f.readv(0, two_buffers, ec), test_word_size);
403 	if (ec)
404 		std::printf("readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
405 	TEST_EQUAL(ec, error_code());
406 	TEST_CHECK(test_buf == "test"_sv);
407 	f.close();
408 }
409 
TORRENT_TEST(hard_link)410 TORRENT_TEST(hard_link)
411 {
412 	// try to create a hard link to see what happens
413 	// first create a regular file to then add another link to.
414 
415 	// create a file, write some stuff to it, create a hard link to that file,
416 	// read that file and assert we get the same stuff we wrote to the first file
417 	error_code ec;
418 	file f;
419 	TEST_CHECK(f.open("original_file", open_mode::read_write, ec));
420 	if (ec)
421 		std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
422 	TEST_EQUAL(ec, error_code());
423 
424 	char str[] = "abcdefghijklmnopqrstuvwxyz";
425 	iovec_t b = { str, 26 };
426 	TEST_EQUAL(f.writev(0, b, ec), 26);
427 	if (ec)
428 		std::printf("writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
429 	TEST_EQUAL(ec, error_code());
430 	f.close();
431 
432 	hard_link("original_file", "second_link", ec);
433 
434 	if (ec)
435 		std::printf("hard_link failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
436 	TEST_EQUAL(ec, error_code());
437 
438 
439 	TEST_CHECK(f.open("second_link", open_mode::read_write, ec));
440 	if (ec)
441 		std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
442 	TEST_EQUAL(ec, error_code());
443 
444 	char test_buf[27] = {};
445 	b = { test_buf, 27 };
446 	TEST_EQUAL(f.readv(0, b, ec), 26);
447 	if (ec)
448 		std::printf("readv failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
449 	TEST_EQUAL(ec, error_code());
450 	TEST_CHECK(test_buf == "abcdefghijklmnopqrstuvwxyz"_sv);
451 	f.close();
452 
453 	remove("original_file", ec);
454 	if (ec)
455 		std::printf("remove failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
456 
457 	remove("second_link", ec);
458 	if (ec)
459 		std::printf("remove failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
460 }
461 
TORRENT_TEST(coalesce_buffer)462 TORRENT_TEST(coalesce_buffer)
463 {
464 	error_code ec;
465 	file f;
466 	TEST_CHECK(f.open("test_file", open_mode::read_write, ec));
467 	if (ec)
468 		std::printf("open failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
469 	TEST_EQUAL(ec, error_code());
470 	if (ec) std::printf("%s\n", ec.message().c_str());
471 	char test[] = "test";
472 	char foobar[] = "foobar";
473 	iovec_t b[2] = {{test, 4}, {foobar, 6}};
474 	TEST_EQUAL(f.writev(0, b, ec, open_mode::coalesce_buffers), 4 + 6);
475 	if (ec)
476 		std::printf("writev failed: [%s] %s\n", ec.category().name(), ec.message().c_str());
477 	TEST_CHECK(!ec);
478 	char test_buf1[5] = {0};
479 	char test_buf2[7] = {0};
480 	b[0] = { test_buf1, 4 };
481 	b[1] = { test_buf2, 6 };
482 	TEST_EQUAL(f.readv(0, b, ec), 4 + 6);
483 	if (ec)
484 	{
485 		std::printf("readv failed: [%s] %s\n"
486 			, ec.category().name(), ec.message().c_str());
487 	}
488 	TEST_EQUAL(ec, error_code());
489 	TEST_CHECK(test_buf1 == "test"_sv);
490 	TEST_CHECK(test_buf2 == "foobar"_sv);
491 	f.close();
492 }
493 
TORRENT_TEST(stat_file)494 TORRENT_TEST(stat_file)
495 {
496 	file_status st;
497 	error_code ec;
498 	stat_file("no_such_file_or_directory.file", &st, ec);
499 	TEST_CHECK(ec);
500 	TEST_EQUAL(ec, boost::system::errc::no_such_file_or_directory);
501 }
502 
TORRENT_TEST(relative_path)503 TORRENT_TEST(relative_path)
504 {
505 #ifdef TORRENT_WINDOWS
506 #define S "\\"
507 #else
508 #define S "/"
509 #endif
510 	TEST_EQUAL(lexically_relative("A" S "B" S "C", "A" S "C" S "B")
511 		, ".." S ".." S "C" S "B");
512 
513 	TEST_EQUAL(lexically_relative("A" S "B" S "C" S, "A" S "C" S "B")
514 		, ".." S ".." S "C" S "B");
515 
516 	TEST_EQUAL(lexically_relative("A" S "B" S "C" S, "A" S "C" S "B" S)
517 		, ".." S ".." S "C" S "B");
518 
519 	TEST_EQUAL(lexically_relative("A" S "B" S "C", "A" S "B" S "B")
520 		, ".." S "B");
521 
522 	TEST_EQUAL(lexically_relative("A" S "B" S "C", "A" S "B" S "C")
523 		, "");
524 
525 	TEST_EQUAL(lexically_relative("A" S "B", "A" S "B")
526 		, "");
527 
528 	TEST_EQUAL(lexically_relative("A" S "B", "A" S "B" S "C")
529 		, "C");
530 
531 	TEST_EQUAL(lexically_relative("A" S, "A" S)
532 		, "");
533 
534 	TEST_EQUAL(lexically_relative("", "A" S "B" S "C")
535 		, "A" S "B" S "C");
536 
537 	TEST_EQUAL(lexically_relative("A" S "B" S "C", "")
538 		, ".." S ".." S ".." S);
539 
540 	TEST_EQUAL(lexically_relative("", ""), "");
541 }
542 
543 // UNC tests
544 #if TORRENT_USE_UNC_PATHS
545 
546 namespace {
current_directory_caps()547 std::tuple<int, bool> current_directory_caps()
548 {
549 #ifdef TORRENT_WINDOWS
550 	DWORD maximum_component_length = FILENAME_MAX;
551 	DWORD file_system_flags = 0;
552 	if (GetVolumeInformation(nullptr
553 		, nullptr, 0, nullptr
554 		, &maximum_component_length, &file_system_flags, nullptr, 0) == 0)
555 	{
556 		error_code ec(GetLastError(), system_category());
557 		std::printf("GetVolumeInformation: [%s : %d] %s\n"
558 			, ec.category().name(), ec.value(), ec.message().c_str());
559 		// ignore errors, this is best-effort
560 	}
561 	bool const support_hard_links = ((file_system_flags & FILE_SUPPORTS_HARD_LINKS) != 0);
562 	return std::make_tuple(int(maximum_component_length), support_hard_links);
563 #else
564 	return std::make_tuple(255, true);
565 #endif
566 }
567 } // anonymous namespace
568 
TORRENT_TEST(unc_tests)569 TORRENT_TEST(unc_tests)
570 {
571 	using lt::canonicalize_path;
572 	TEST_EQUAL(canonicalize_path("c:\\a\\..\\b"), "c:\\b");
573 	TEST_EQUAL(canonicalize_path("a\\..\\b"), "b");
574 	TEST_EQUAL(canonicalize_path("a\\..\\.\\b"), "b");
575 	TEST_EQUAL(canonicalize_path("\\.\\a"), "\\a");
576 	TEST_EQUAL(canonicalize_path("\\\\bla\\.\\a"), "\\\\bla\\a");
577 	TEST_EQUAL(canonicalize_path("c:\\bla\\a"), "c:\\bla\\a");
578 
579 	error_code ec;
580 
581 	std::vector<std::string> special_names
582 	{
583 		"CON", "PRN", "AUX", "NUL",
584 		"COM1", "COM2", "COM3", "COM4",
585 		"COM5", "COM6", "COM7", "COM8",
586 		"COM9", "LPT1", "LPT2", "LPT3",
587 		"LPT4", "LPT5", "LPT6", "LPT7",
588 		"LPT8", "LPT9"
589 	};
590 
591 	for (std::string special_name : special_names)
592 	{
593 		touch_file(special_name, 10);
594 		TEST_CHECK(lt::exists(special_name));
595 		lt::remove(special_name, ec);
596 		TEST_EQUAL(ec, error_code());
597 		TEST_CHECK(!lt::exists(special_name));
598 	}
599 
600 	int maximum_component_length;
601 	bool support_hard_links;
602 	std::tie(maximum_component_length, support_hard_links) = current_directory_caps();
603 
604 	std::cout << "max file path component length: " << maximum_component_length << "\n"
605 		<< "support hard links: " << (support_hard_links?"yes":"no") << "\n";
606 
607 	std::string long_dir_name;
608 	maximum_component_length -= 12;
609 	long_dir_name.resize(size_t(maximum_component_length));
610 	for (int i = 0; i < maximum_component_length; ++i)
611 		long_dir_name[i] = static_cast<char>((i % 26) + 'A');
612 
613 	std::string long_file_name1 = combine_path(long_dir_name, long_dir_name);
614 	long_file_name1.back() = '1';
615 	std::string long_file_name2 = long_file_name1;
616 	long_file_name2.back() = '2';
617 
618 	lt::create_directory(long_dir_name, ec);
619 	TEST_EQUAL(ec, error_code());
620 	if (ec)
621 	{
622 		std::cout << "create_directory \"" << long_dir_name << "\" failed: " << ec.message() << "\n";
623 		std::wcout << convert_to_native_path_string(long_dir_name) << L"\n";
624 	}
625 	TEST_CHECK(lt::exists(long_dir_name));
626 	TEST_CHECK(lt::is_directory(long_dir_name, ec));
627 	TEST_EQUAL(ec, error_code());
628 	if (ec)
629 	{
630 		std::cout << "is_directory \"" << long_dir_name << "\" failed " << ec.message() << "\n";
631 		std::wcout << convert_to_native_path_string(long_dir_name) << L"\n";
632 	}
633 
634 	touch_file(long_file_name1, 10);
635 	TEST_CHECK(lt::exists(long_file_name1));
636 
637 	lt::rename(long_file_name1, long_file_name2, ec);
638 	TEST_EQUAL(ec, error_code());
639 	if (ec)
640 	{
641 		std::cout << "rename \"" << long_file_name1 << "\" failed " << ec.message() << "\n";
642 		std::wcout << convert_to_native_path_string(long_file_name1) << L"\n";
643 	}
644 	TEST_CHECK(!lt::exists(long_file_name1));
645 	TEST_CHECK(lt::exists(long_file_name2));
646 
647 	lt::copy_file(long_file_name2, long_file_name1, ec);
648 	TEST_EQUAL(ec, error_code());
649 	if (ec)
650 	{
651 		std::cout << "copy_file \"" << long_file_name2 << "\" failed " << ec.message() << "\n";
652 		std::wcout << convert_to_native_path_string(long_file_name2) << L"\n";
653 	}
654 	TEST_CHECK(lt::exists(long_file_name1));
655 
656 	std::set<std::string> files;
657 
658 	for (lt::directory i(long_dir_name, ec); !i.done(); i.next(ec))
659 	{
660 		std::string f = i.file();
661 		files.insert(f);
662 	}
663 
664 	TEST_EQUAL(files.size(), 4);
665 
666 	lt::remove(long_file_name1, ec);
667 	TEST_EQUAL(ec, error_code());
668 	if (ec)
669 	{
670 		std::cout << "remove \"" << long_file_name1 << "\" failed " << ec.message() << "\n";
671 		std::wcout << convert_to_native_path_string(long_file_name1) << L"\n";
672 	}
673 	TEST_CHECK(!lt::exists(long_file_name1));
674 
675 	if (support_hard_links)
676 	{
677 		lt::hard_link(long_file_name2, long_file_name1, ec);
678 		TEST_EQUAL(ec, error_code());
679 		TEST_CHECK(lt::exists(long_file_name1));
680 
681 		lt::remove(long_file_name1, ec);
682 		TEST_EQUAL(ec, error_code());
683 		TEST_CHECK(!lt::exists(long_file_name1));
684 	}
685 }
686 
TORRENT_TEST(unc_paths)687 TORRENT_TEST(unc_paths)
688 {
689 	std::string const reserved_name = "con";
690 	error_code ec;
691 	{
692 		file f;
693 		TEST_CHECK(f.open(reserved_name, open_mode::read_write, ec) && !ec);
694 	}
695 	remove(reserved_name, ec);
696 	TEST_CHECK(!ec);
697 }
698 
699 #endif
700