]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/tests/unit/file_io_test.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / seastar / tests / unit / file_io_test.cc
CommitLineData
11fdf7f2
TL
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright (C) 2014-2015 Cloudius Systems, Ltd.
20 */
21
22#include <seastar/testing/test_case.hh>
9f95a23c 23#include <seastar/testing/thread_test_case.hh>
f67539c2 24#include <seastar/testing/test_runner.hh>
11fdf7f2 25
f67539c2 26#include <seastar/core/seastar.hh>
11fdf7f2
TL
27#include <seastar/core/semaphore.hh>
28#include <seastar/core/condition-variable.hh>
29#include <seastar/core/file.hh>
f67539c2 30#include <seastar/core/layered_file.hh>
11fdf7f2
TL
31#include <seastar/core/thread.hh>
32#include <seastar/core/stall_sampler.hh>
f67539c2
TL
33#include <seastar/core/aligned_buffer.hh>
34#include <seastar/util/tmp_file.hh>
35#include <seastar/util/alloc_failure_injector.hh>
36
9f95a23c 37#include <boost/range/adaptor/transformed.hpp>
11fdf7f2 38#include <iostream>
f67539c2 39#include <sys/statfs.h>
11fdf7f2 40
9f95a23c
TL
41#include "core/file-impl.hh"
42
11fdf7f2 43using namespace seastar;
f67539c2 44namespace fs = std::filesystem;
11fdf7f2
TL
45
46SEASTAR_TEST_CASE(open_flags_test) {
47 open_flags flags = open_flags::rw | open_flags::create | open_flags::exclusive;
48 BOOST_REQUIRE(std::underlying_type_t<open_flags>(flags) ==
49 (std::underlying_type_t<open_flags>(open_flags::rw) |
50 std::underlying_type_t<open_flags>(open_flags::create) |
51 std::underlying_type_t<open_flags>(open_flags::exclusive)));
52
53 open_flags mask = open_flags::create | open_flags::exclusive;
54 BOOST_REQUIRE((flags & mask) == mask);
55 return make_ready_future<>();
56}
57
9f95a23c
TL
58SEASTAR_TEST_CASE(access_flags_test) {
59 access_flags flags = access_flags::read | access_flags::write | access_flags::execute;
60 BOOST_REQUIRE(std::underlying_type_t<open_flags>(flags) ==
61 (std::underlying_type_t<open_flags>(access_flags::read) |
62 std::underlying_type_t<open_flags>(access_flags::write) |
63 std::underlying_type_t<open_flags>(access_flags::execute)));
64 return make_ready_future<>();
65}
66
67SEASTAR_TEST_CASE(file_exists_test) {
f67539c2
TL
68 return tmp_dir::do_with_thread([] (tmp_dir& t) {
69 sstring filename = (t.get_path() / "testfile.tmp").native();
9f95a23c
TL
70 auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0();
71 f.close().get();
72 auto exists = file_exists(filename).get0();
73 BOOST_REQUIRE(exists);
74 remove_file(filename).get();
75 exists = file_exists(filename).get0();
76 BOOST_REQUIRE(!exists);
77 });
78}
79
f67539c2
TL
80SEASTAR_TEST_CASE(handle_bad_alloc_test) {
81 return tmp_dir::do_with_thread([] (tmp_dir& t) {
82 sstring filename = (t.get_path() / "testfile.tmp").native();
83 auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0();
84 f.close().get();
85 bool exists = false;
86 memory::with_allocation_failures([&] {
87 exists = file_exists(filename).get0();
88 });
89 BOOST_REQUIRE(exists);
90 });
91}
92
9f95a23c 93SEASTAR_TEST_CASE(file_access_test) {
f67539c2
TL
94 return tmp_dir::do_with_thread([] (tmp_dir& t) {
95 sstring filename = (t.get_path() / "testfile.tmp").native();
9f95a23c
TL
96 auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0();
97 f.close().get();
98 auto is_accessible = file_accessible(filename, access_flags::read | access_flags::write).get0();
99 BOOST_REQUIRE(is_accessible);
9f95a23c
TL
100 });
101}
102
11fdf7f2
TL
103struct file_test {
104 file_test(file&& f) : f(std::move(f)) {}
105 file f;
106 semaphore sem = { 0 };
107 semaphore par = { 1000 };
108};
109
110SEASTAR_TEST_CASE(test1) {
111 // Note: this tests generates a file "testfile.tmp" with size 4096 * max (= 40 MB).
f67539c2 112 return tmp_dir::do_with([] (tmp_dir& t) {
11fdf7f2 113 static constexpr auto max = 10000;
f67539c2
TL
114 sstring filename = (t.get_path() / "testfile.tmp").native();
115 return open_file_dma(filename, open_flags::rw | open_flags::create).then([filename] (file f) {
11fdf7f2
TL
116 auto ft = new file_test{std::move(f)};
117 for (size_t i = 0; i < max; ++i) {
9f95a23c
TL
118 // Don't wait for future, use semaphore to signal when done instead.
119 (void)ft->par.wait().then([ft, i] {
11fdf7f2
TL
120 auto wbuf = allocate_aligned_buffer<unsigned char>(4096, 4096);
121 std::fill(wbuf.get(), wbuf.get() + 4096, i);
122 auto wb = wbuf.get();
9f95a23c 123 (void)ft->f.dma_write(i * 4096, wb, 4096).then(
11fdf7f2
TL
124 [ft, i, wbuf = std::move(wbuf)] (size_t ret) mutable {
125 BOOST_REQUIRE(ret == 4096);
126 auto rbuf = allocate_aligned_buffer<unsigned char>(4096, 4096);
127 auto rb = rbuf.get();
9f95a23c 128 (void)ft->f.dma_read(i * 4096, rb, 4096).then(
11fdf7f2
TL
129 [ft, rbuf = std::move(rbuf), wbuf = std::move(wbuf)] (size_t ret) mutable {
130 BOOST_REQUIRE(ret == 4096);
131 BOOST_REQUIRE(std::equal(rbuf.get(), rbuf.get() + 4096, wbuf.get()));
132 ft->sem.signal(1);
133 ft->par.signal();
134 });
135 });
136 });
137 }
138 return ft->sem.wait(max).then([ft] () mutable {
139 return ft->f.flush();
140 }).then([ft] {
141 return ft->f.close();
142 }).then([ft] () mutable {
11fdf7f2
TL
143 delete ft;
144 });
145 });
f67539c2 146 });
11fdf7f2
TL
147}
148
149SEASTAR_TEST_CASE(parallel_write_fsync) {
150 return internal::report_reactor_stalls([] {
f67539c2 151 return tmp_dir::do_with_thread([] (tmp_dir& t) {
11fdf7f2 152 // Plan: open a file and write to it like crazy. In parallel fsync() it all the time.
f67539c2 153 auto fname = (t.get_path() / "testfile.tmp").native();
11fdf7f2
TL
154 auto sz = uint64_t(32*1024*1024);
155 auto buffer_size = 32768;
156 auto write_concurrency = 16;
157 auto fsync_every = 1024*1024;
158 auto max_write_ahead_of_fsync = 4*1024*1024; // ensures writes don't complete too quickly
159 auto written = uint64_t(0);
160 auto fsynced_at = uint64_t(0);
161
162 file f = open_file_dma(fname, open_flags::rw | open_flags::create | open_flags::truncate).get0();
163 // Avoid filesystem problems with size-extending operations
164 f.truncate(sz).get();
165
166 auto fsync_semaphore = semaphore(0);
167 auto may_write_condvar = condition_variable();
168 auto fsync_thread = thread([&] {
169 auto fsynced = uint64_t(0);
170 while (fsynced < sz) {
171 fsync_semaphore.wait(fsync_every).get();
172 fsynced_at = written;
173 // Signal the condition variable now so that writes proceed
174 // in parallel with the fsync
175 may_write_condvar.broadcast();
176 f.flush().get();
177 fsynced += fsync_every;
178 }
179 });
180
181 auto write_semaphore = semaphore(write_concurrency);
182 while (written < sz) {
183 write_semaphore.wait().get();
184 may_write_condvar.wait([&] {
185 return written <= fsynced_at + max_write_ahead_of_fsync;
186 }).get();
187 auto buf = temporary_buffer<char>::aligned(f.memory_dma_alignment(), buffer_size);
f67539c2 188 memset(buf.get_write(), 0, buf.size());
9f95a23c
TL
189 // Write asynchronously, signal when done.
190 (void)f.dma_write(written, buf.get(), buf.size()).then([&fsync_semaphore, &write_semaphore, buf = std::move(buf)] (size_t w) {
11fdf7f2
TL
191 fsync_semaphore.signal(buf.size());
192 write_semaphore.signal();
193 });
194 written += buffer_size;
195 }
196 write_semaphore.wait(write_concurrency).get();
197
198 fsync_thread.join().get();
199 f.close().get();
200 remove_file(fname).get();
201 });
202 }).then([] (internal::stall_report sr) {
203 std::cout << "parallel_write_fsync: " << sr << "\n";
204 });
205}
9f95a23c 206
f67539c2
TL
207SEASTAR_TEST_CASE(test_iov_max) {
208 return tmp_dir::do_with_thread([] (tmp_dir& t) {
9f95a23c
TL
209 static constexpr size_t buffer_size = 4096;
210 static constexpr size_t buffer_count = IOV_MAX * 2 + 1;
211
212 std::vector<temporary_buffer<char>> original_buffers;
213 std::vector<iovec> iovecs;
214 for (auto i = 0u; i < buffer_count; i++) {
215 original_buffers.emplace_back(temporary_buffer<char>::aligned(buffer_size, buffer_size));
216 std::fill_n(original_buffers.back().get_write(), buffer_size, char(i));
217 iovecs.emplace_back(iovec { original_buffers.back().get_write(), buffer_size });
218 }
219
f67539c2
TL
220 auto filename = (t.get_path() / "testfile.tmp").native();
221 auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0();
9f95a23c
TL
222 size_t left = buffer_size * buffer_count;
223 size_t position = 0;
224 while (left) {
225 auto written = f.dma_write(position, iovecs).get0();
226 iovecs.erase(iovecs.begin(), iovecs.begin() + written / buffer_size);
227 assert(written % buffer_size == 0);
228 position += written;
229 left -= written;
230 }
231
232 BOOST_CHECK(iovecs.empty());
233
234 std::vector<temporary_buffer<char>> read_buffers;
235 for (auto i = 0u; i < buffer_count; i++) {
236 read_buffers.emplace_back(temporary_buffer<char>::aligned(buffer_size, buffer_size));
237 std::fill_n(read_buffers.back().get_write(), buffer_size, char(0));
238 iovecs.emplace_back(iovec { read_buffers.back().get_write(), buffer_size });
239 }
240
241 left = buffer_size * buffer_count;
242 position = 0;
243 while (left) {
244 auto read = f.dma_read(position, iovecs).get0();
245 iovecs.erase(iovecs.begin(), iovecs.begin() + read / buffer_size);
246 assert(read % buffer_size == 0);
247 position += read;
248 left -= read;
249 }
250
251 for (auto i = 0u; i < buffer_count; i++) {
252 BOOST_CHECK(std::equal(original_buffers[i].get(), original_buffers[i].get() + original_buffers[i].size(),
253 read_buffers[i].get(), read_buffers[i].get() + read_buffers[i].size()));
254 }
255
256 f.close().get();
f67539c2 257 });
9f95a23c
TL
258}
259
260SEASTAR_THREAD_TEST_CASE(test_sanitize_iovecs) {
261 auto buf = temporary_buffer<char>::aligned(4096, 4096);
262
263 auto iovec_equal = [] (const iovec& a, const iovec& b) {
264 return a.iov_base == b.iov_base && a.iov_len == b.iov_len;
265 };
266
267 { // Single fragment, sanitize is noop
268 auto original_iovecs = std::vector<iovec> { { buf.get_write(), buf.size() } };
269 auto actual_iovecs = original_iovecs;
270 auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096);
271 BOOST_CHECK_EQUAL(actual_length, 4096);
272 BOOST_CHECK_EQUAL(actual_iovecs.size(), 1);
273 BOOST_CHECK(iovec_equal(original_iovecs.back(), actual_iovecs.back()));
274 }
275
276 { // one 1024 buffer and IOV_MAX+6 buffers of 512; 4096 byte disk alignment, sanitize needs to drop buffers
277 auto original_iovecs = std::vector<iovec>{};
278 for (auto i = 0u; i < IOV_MAX + 7; i++) {
279 original_iovecs.emplace_back(iovec { buf.get_write(), i == 0 ? 1024u : 512u });
280 }
281 auto actual_iovecs = original_iovecs;
282 auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096);
283 BOOST_CHECK_EQUAL(actual_length, 512 * IOV_MAX);
284 BOOST_CHECK_EQUAL(actual_iovecs.size(), IOV_MAX - 1);
285
286 original_iovecs.resize(IOV_MAX - 1);
287 BOOST_CHECK(std::equal(original_iovecs.begin(), original_iovecs.end(),
288 actual_iovecs.begin(), actual_iovecs.end(), iovec_equal));
289 }
290
291 { // IOV_MAX-1 buffers of 512, one 1024 buffer, and 6 512 buffers; 4096 byte disk alignment, sanitize needs to drop and trim buffers
292 auto original_iovecs = std::vector<iovec>{};
293 for (auto i = 0u; i < IOV_MAX + 7; i++) {
294 original_iovecs.emplace_back(iovec { buf.get_write(), i == (IOV_MAX - 1) ? 1024u : 512u });
295 }
296 auto actual_iovecs = original_iovecs;
297 auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096);
298 BOOST_CHECK_EQUAL(actual_length, 512 * IOV_MAX);
299 BOOST_CHECK_EQUAL(actual_iovecs.size(), IOV_MAX);
300
301 original_iovecs.resize(IOV_MAX);
302 original_iovecs.back().iov_len = 512;
303 BOOST_CHECK(std::equal(original_iovecs.begin(), original_iovecs.end(),
304 actual_iovecs.begin(), actual_iovecs.end(), iovec_equal));
305 }
306
307 { // IOV_MAX+8 buffers of 512; 4096 byte disk alignment, sanitize needs to drop buffers
308 auto original_iovecs = std::vector<iovec>{};
309 for (auto i = 0u; i < IOV_MAX + 8; i++) {
310 original_iovecs.emplace_back(iovec { buf.get_write(), 512 });
311 }
312 auto actual_iovecs = original_iovecs;
313 auto actual_length = internal::sanitize_iovecs(actual_iovecs, 4096);
314 BOOST_CHECK_EQUAL(actual_length, 512 * IOV_MAX);
315 BOOST_CHECK_EQUAL(actual_iovecs.size(), IOV_MAX);
316
317 original_iovecs.resize(IOV_MAX);
318 BOOST_CHECK(std::equal(original_iovecs.begin(), original_iovecs.end(),
319 actual_iovecs.begin(), actual_iovecs.end(), iovec_equal));
320 }
321}
322
f67539c2
TL
323SEASTAR_TEST_CASE(test_chmod) {
324 return tmp_dir::do_with_thread([] (tmp_dir& t) {
9f95a23c 325 auto oflags = open_flags::rw | open_flags::create;
f67539c2 326 sstring filename = (t.get_path() / "testfile.tmp").native();
9f95a23c
TL
327 if (file_exists(filename).get0()) {
328 remove_file(filename).get();
329 }
330
331 auto orig_umask = umask(0);
332
333 // test default_file_permissions
334 auto f = open_file_dma(filename, oflags).get0();
335 f.close().get();
336 auto sd = file_stat(filename).get0();
337 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions));
338
339 // test chmod with new_permissions
340 auto new_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read;
341 BOOST_REQUIRE(new_permissions != file_permissions::default_file_permissions);
342 BOOST_REQUIRE(file_exists(filename).get0());
343 chmod(filename, new_permissions).get();
344 sd = file_stat(filename).get0();
345 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(new_permissions));
346 remove_file(filename).get();
347
348 umask(orig_umask);
f67539c2 349 });
9f95a23c
TL
350}
351
f67539c2
TL
352SEASTAR_TEST_CASE(test_open_file_dma_permissions) {
353 return tmp_dir::do_with_thread([] (tmp_dir& t) {
9f95a23c 354 auto oflags = open_flags::rw | open_flags::create;
f67539c2 355 sstring filename = (t.get_path() / "testfile.tmp").native();
9f95a23c
TL
356 if (file_exists(filename).get0()) {
357 remove_file(filename).get();
358 }
359
360 auto orig_umask = umask(0);
361
362 // test default_file_permissions
363 auto f = open_file_dma(filename, oflags).get0();
364 f.close().get();
365 auto sd = file_stat(filename).get0();
366 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions));
367 remove_file(filename).get();
368
369 // test options.create_permissions
370 auto options = file_open_options();
371 options.create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read;
372 BOOST_REQUIRE(options.create_permissions != file_permissions::default_file_permissions);
373 f = open_file_dma(filename, oflags, options).get0();
374 f.close().get();
375 sd = file_stat(filename).get0();
376 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(options.create_permissions));
377 remove_file(filename).get();
378
379 umask(orig_umask);
f67539c2 380 });
9f95a23c
TL
381}
382
f67539c2
TL
383SEASTAR_TEST_CASE(test_make_directory_permissions) {
384 return tmp_dir::do_with_thread([] (tmp_dir& t) {
385 sstring dirname = (t.get_path() / "testdir.tmp").native();
9f95a23c
TL
386 auto orig_umask = umask(0);
387
388 // test default_dir_permissions with make_directory
389 make_directory(dirname).get();
390 auto sd = file_stat(dirname).get0();
391 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions));
392 remove_file(dirname).get();
393
394 // test make_directory
395 auto create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read;
396 BOOST_REQUIRE(create_permissions != file_permissions::default_dir_permissions);
397 make_directory(dirname, create_permissions).get();
398 sd = file_stat(dirname).get0();
399 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions));
400 remove_file(dirname).get();
401
402 umask(orig_umask);
f67539c2 403 });
9f95a23c
TL
404}
405
f67539c2
TL
406SEASTAR_TEST_CASE(test_touch_directory_permissions) {
407 return tmp_dir::do_with_thread([] (tmp_dir& t) {
408 sstring dirname = (t.get_path() / "testdir.tmp").native();
9f95a23c
TL
409 auto orig_umask = umask(0);
410
411 // test default_dir_permissions with touch_directory
412 touch_directory(dirname).get();
413 auto sd = file_stat(dirname).get0();
414 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions));
415 remove_file(dirname).get();
416
417 // test touch_directory, dir creation
418 auto create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read;
419 BOOST_REQUIRE(create_permissions != file_permissions::default_dir_permissions);
420 BOOST_REQUIRE(!file_exists(dirname).get0());
421 touch_directory(dirname, create_permissions).get();
422 sd = file_stat(dirname).get0();
423 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions));
424
425 // test touch_directory of existing dir, dir mode need not change
426 BOOST_REQUIRE(file_exists(dirname).get0());
427 touch_directory(dirname, file_permissions::default_dir_permissions).get();
428 sd = file_stat(dirname).get0();
429 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions));
430 remove_file(dirname).get();
431
432 umask(orig_umask);
f67539c2 433 });
9f95a23c
TL
434}
435
f67539c2
TL
436SEASTAR_TEST_CASE(test_recursive_touch_directory_permissions) {
437 return tmp_dir::do_with_thread([] (tmp_dir& t) {
438 sstring base_dirname = (t.get_path() / "testbasedir.tmp").native();
439 sstring dirpath = base_dirname + "/" + "testsubdir.tmp";
9f95a23c
TL
440 if (file_exists(dirpath).get0()) {
441 remove_file(dirpath).get();
442 }
443 if (file_exists(base_dirname).get0()) {
444 remove_file(base_dirname).get();
445 }
446
447 auto orig_umask = umask(0);
448
449 // test default_dir_permissions with recursive_touch_directory
450 recursive_touch_directory(dirpath).get();
451 auto sd = file_stat(base_dirname).get0();
452 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions));
453 sd = file_stat(dirpath).get0();
454 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions));
455 remove_file(dirpath).get();
456
457 // test recursive_touch_directory, dir creation
458 auto create_permissions = file_permissions::user_read | file_permissions::group_read | file_permissions::others_read;
459 BOOST_REQUIRE(create_permissions != file_permissions::default_dir_permissions);
460 BOOST_REQUIRE(file_exists(base_dirname).get0());
461 BOOST_REQUIRE(!file_exists(dirpath).get0());
462 recursive_touch_directory(dirpath, create_permissions).get();
463 sd = file_stat(base_dirname).get0();
464 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions));
465 sd = file_stat(dirpath).get0();
466 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions));
467
468 // test recursive_touch_directory of existing dir, dir mode need not change
469 BOOST_REQUIRE(file_exists(dirpath).get0());
470 recursive_touch_directory(dirpath, file_permissions::default_dir_permissions).get();
471 sd = file_stat(base_dirname).get0();
472 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_dir_permissions));
473 sd = file_stat(dirpath).get0();
474 BOOST_CHECK_EQUAL(sd.mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(create_permissions));
475 remove_file(dirpath).get();
476 remove_file(base_dirname).get();
477
478 umask(orig_umask);
f67539c2
TL
479 });
480}
481
482SEASTAR_TEST_CASE(test_file_stat_method) {
483 return tmp_dir::do_with_thread([] (tmp_dir& t) {
484 auto oflags = open_flags::rw | open_flags::create;
485 sstring filename = (t.get_path() / "testfile.tmp").native();
486
487 auto orig_umask = umask(0);
488
489 auto f = open_file_dma(filename, oflags).get0();
490 auto st = f.stat().get0();
491 f.close().get();
492 BOOST_CHECK_EQUAL(st.st_mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions));
493
494 umask(orig_umask);
495 });
496}
497
498
499class test_layered_file : public layered_file_impl {
500public:
501 explicit test_layered_file(file f) : layered_file_impl(std::move(f)) {}
502 virtual future<size_t> write_dma(uint64_t pos, const void* buffer, size_t len, const io_priority_class& pc) override {
503 abort();
504 }
505 virtual future<size_t> write_dma(uint64_t pos, std::vector<iovec> iov, const io_priority_class& pc) override {
506 abort();
507 }
508 virtual future<size_t> read_dma(uint64_t pos, void* buffer, size_t len, const io_priority_class& pc) override {
509 abort();
510 }
511 virtual future<size_t> read_dma(uint64_t pos, std::vector<iovec> iov, const io_priority_class& pc) override {
512 abort();
513 }
514 virtual future<> flush(void) override {
515 abort();
516 }
517 virtual future<struct stat> stat(void) override {
518 abort();
519 }
520 virtual future<> truncate(uint64_t length) override {
521 abort();
522 }
523 virtual future<> discard(uint64_t offset, uint64_t length) override {
524 abort();
525 }
526 virtual future<> allocate(uint64_t position, uint64_t length) override {
527 abort();
528 }
529 virtual future<uint64_t> size(void) override {
530 abort();
531 }
532 virtual future<> close() override {
533 abort();
534 }
535 virtual std::unique_ptr<file_handle_impl> dup() override {
536 abort();
537 }
538 virtual subscription<directory_entry> list_directory(std::function<future<> (directory_entry de)> next) override {
539 abort();
540 }
541 virtual future<temporary_buffer<uint8_t>> dma_read_bulk(uint64_t offset, size_t range_size, const io_priority_class& pc) override {
542 abort();
543 }
544};
545
546SEASTAR_TEST_CASE(test_underlying_file) {
547 return tmp_dir::do_with_thread([] (tmp_dir& t) {
548 auto oflags = open_flags::rw | open_flags::create;
549 sstring filename = (t.get_path() / "testfile.tmp").native();
550 auto f = open_file_dma(filename, oflags).get0();
551 auto lf = file(make_shared<test_layered_file>(f));
552 BOOST_CHECK_EQUAL(f.memory_dma_alignment(), lf.memory_dma_alignment());
553 BOOST_CHECK_EQUAL(f.disk_read_dma_alignment(), lf.disk_read_dma_alignment());
554 BOOST_CHECK_EQUAL(f.disk_write_dma_alignment(), lf.disk_write_dma_alignment());
555 f.close().get();
556 });
557}
558
559SEASTAR_TEST_CASE(test_file_stat_method_with_file) {
560 return tmp_dir::do_with_thread([] (tmp_dir& t) {
561 auto oflags = open_flags::rw | open_flags::create | open_flags::truncate;
562 sstring filename = (t.get_path() / "testfile.tmp").native();
563 file ref;
564
565 auto orig_umask = umask(0);
566
567 auto st = with_file(open_file_dma(filename, oflags), [&ref] (file& f) {
568 // make a copy of f to verify f is auto-closed when `with_file` returns.
569 ref = f;
570 return f.stat();
571 }).get0();
572 BOOST_CHECK_EQUAL(st.st_mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions));
573
574 // verify that the file was auto-closed
575 BOOST_REQUIRE_THROW(ref.stat().get(), std::system_error);
576
577 umask(orig_umask);
578 });
579}
580
581SEASTAR_TEST_CASE(test_open_error_with_file) {
582 return tmp_dir::do_with_thread([] (tmp_dir& t) {
583 auto open_file = [&t] (bool do_open) {
584 auto oflags = open_flags::ro;
585 sstring filename = (t.get_path() / "testfile.tmp").native();
586 if (do_open) {
587 return open_file_dma(filename, oflags);
588 } else {
589 throw std::runtime_error("expected exception");
590 }
591 };
592 bool got_exception = false;
593
594 BOOST_REQUIRE_NO_THROW(with_file(open_file(true), [] (file& f) {
595 BOOST_REQUIRE(false);
596 }).handle_exception_type([&got_exception] (const std::system_error& e) {
597 got_exception = true;
598 BOOST_REQUIRE(e.code().value() == ENOENT);
599 }).get());
600 BOOST_REQUIRE(got_exception);
601
602 got_exception = false;
603 BOOST_REQUIRE_THROW(with_file(open_file(false), [] (file& f) {
604 BOOST_REQUIRE(false);
605 }).handle_exception_type([&got_exception] (const std::runtime_error& e) {
606 got_exception = true;
607 }).get(), std::runtime_error);
608 BOOST_REQUIRE(!got_exception);
609 });
610}
611
612SEASTAR_TEST_CASE(test_with_file_close_on_failure) {
613 return tmp_dir::do_with_thread([] (tmp_dir& t) {
614 auto oflags = open_flags::rw | open_flags::create | open_flags::truncate;
615 sstring filename = (t.get_path() / "testfile.tmp").native();
616
617 auto orig_umask = umask(0);
618
619 // error-free case
620 auto ref = with_file_close_on_failure(open_file_dma(filename, oflags), [] (file& f) {
621 return f;
622 }).get0();
623 auto st = ref.stat().get0();
624 ref.close().get();
625 BOOST_CHECK_EQUAL(st.st_mode & static_cast<mode_t>(file_permissions::all_permissions), static_cast<mode_t>(file_permissions::default_file_permissions));
626
627 // close-on-error case
628 BOOST_REQUIRE_THROW(with_file_close_on_failure(open_file_dma(filename, oflags), [&ref] (file& f) {
629 ref = f;
630 throw std::runtime_error("expected exception");
631 }).get(), std::runtime_error);
632
633 // verify that file was auto-closed on error
634 BOOST_REQUIRE_THROW(ref.stat().get(), std::system_error);
635
636 umask(orig_umask);
637 });
638}
639
640namespace seastar {
641 extern bool aio_nowait_supported;
642}
643
644SEASTAR_TEST_CASE(test_nowait_flag_correctness) {
645 return tmp_dir::do_with_thread([] (tmp_dir& t) {
646 auto oflags = open_flags::rw | open_flags::create;
647 sstring filename = (t.get_path() / "testfile.tmp").native();
648 auto is_tmpfs = [&] (sstring filename) {
649 struct ::statfs buf;
650 int fd = ::open(filename.c_str(), static_cast<int>(open_flags::ro));
651 assert(fd != -1);
652 auto r = ::fstatfs(fd, &buf);
653 if (r == -1) {
654 return false;
655 }
656 return buf.f_type == 0x01021994; // TMPFS_MAGIC
657 };
658
659 if (!seastar::aio_nowait_supported) {
660 BOOST_TEST_WARN(0, "Skipping this test because RWF_NOWAIT is not supported by the system");
661 return;
662 }
663
664 auto f = open_file_dma(filename, oflags).get0();
665 auto close_f = defer([&] { f.close().get(); });
666
667 if (is_tmpfs(filename)) {
668 BOOST_TEST_WARN(0, "Skipping this test because TMPFS was detected, and RWF_NOWAIT is only supported by disk-based FSes");
669 return;
670 }
671
672 for (auto i = 0; i < 10; i++) {
673 auto wbuf = allocate_aligned_buffer<unsigned char>(4096, 4096);
674 std::fill(wbuf.get(), wbuf.get() + 4096, i);
675 auto wb = wbuf.get();
676 f.dma_write(i * 4096, wb, 4096).get();
677 f.flush().get0();
678 }
679 });
680}
681
682SEASTAR_TEST_CASE(test_destruct_just_constructed_append_challenged_file) {
683 return tmp_dir::do_with_thread([] (tmp_dir& t) {
684 sstring filename = (t.get_path() / "testfile.tmp").native();
685 auto oflags = open_flags::rw | open_flags::create;
686 auto f = open_file_dma(filename, oflags).get0();
687 });
688}
689
690SEASTAR_TEST_CASE(test_destruct_append_challenged_file_after_write) {
691 return tmp_dir::do_with_thread([] (tmp_dir& t) {
692 sstring filename = (t.get_path() / "testfile.tmp").native();
693 auto buf = allocate_aligned_buffer<unsigned char>(4096, 4096);
694 std::fill(buf.get(), buf.get() + 4096, 0);
695
696 auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0();
697 f.dma_write(0, buf.get(), 4096).get();
698 });
699}
700
701SEASTAR_TEST_CASE(test_destruct_append_challenged_file_after_read) {
702 return tmp_dir::do_with_thread([] (tmp_dir& t) {
703 sstring filename = (t.get_path() / "testfile.tmp").native();
704 auto buf = allocate_aligned_buffer<unsigned char>(4096, 4096);
705 std::fill(buf.get(), buf.get() + 4096, 0);
706
707 auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0();
708 f.dma_write(0, buf.get(), 4096).get();
709 f.flush().get0();
710 f.close().get();
711
712 f = open_file_dma(filename, open_flags::rw).get0();
713 f.dma_read(0, buf.get(), 4096).get();
714 });
715}
716
717SEASTAR_TEST_CASE(test_dma_iovec) {
718 return tmp_dir::do_with_thread([] (tmp_dir& t) {
719 static constexpr size_t alignment = 4096;
720 auto wbuf = allocate_aligned_buffer<char>(alignment, alignment);
721 size_t size = 1234;
722 std::fill_n(wbuf.get(), alignment, char(0));
723 std::fill_n(wbuf.get(), size, char(42));
724 std::vector<iovec> iovecs;
725
726 auto filename = (t.get_path() / "testfile.tmp").native();
727 auto f = open_file_dma(filename, open_flags::rw | open_flags::create).get0();
728 iovecs.push_back(iovec{ wbuf.get(), alignment });
729 auto count = f.dma_write(0, iovecs).get0();
730 BOOST_REQUIRE_EQUAL(count, alignment);
731 f.truncate(size).get();
732 f.close().get();
733
734 auto rbuf = allocate_aligned_buffer<char>(alignment, alignment);
735
736 // this tests the posix_file_impl
737 f = open_file_dma(filename, open_flags::ro).get0();
738 std::fill_n(rbuf.get(), alignment, char(0));
739 iovecs.clear();
740 iovecs.push_back(iovec{ rbuf.get(), alignment });
741 count = f.dma_read(0, iovecs).get0();
742 BOOST_REQUIRE_EQUAL(count, size);
743
744 BOOST_REQUIRE(std::equal(wbuf.get(), wbuf.get() + alignment, rbuf.get(), rbuf.get() + alignment));
745
746 // this tests the append_challenged_posix_file_impl
747 f = open_file_dma(filename, open_flags::rw).get0();
748 std::fill_n(rbuf.get(), alignment, char(0));
749 iovecs.clear();
750 iovecs.push_back(iovec{ rbuf.get(), alignment });
751 count = f.dma_read(0, iovecs).get0();
752 BOOST_REQUIRE_EQUAL(count, size);
753
754 BOOST_REQUIRE(std::equal(wbuf.get(), wbuf.get() + alignment, rbuf.get(), rbuf.get() + alignment));
755 });
9f95a23c 756}