ewmscp  ..
fileOpsTests.cpp
Go to the documentation of this file.
1 #include <fcntl.h>
2 #include <stdio.h>
3 #include <string.h>
4 #include <sys/mman.h>
5 #include <sys/stat.h>
6 #include <sys/types.h>
7 #include <unistd.h>
8 #ifdef withGpfs
9 #include <gpfs.h>
10 #endif
11 
12 #include "throwcall.h"
13 #include <OptionsChrono.h>
14 #include <algorithm>
15 #include <cmath>
16 #include <list>
17 #include <numeric>
18 #include <set>
19 #include <thread>
20 
26 typedef std::chrono::high_resolution_clock clockType;
27 std::ostream& operator<< (std::ostream& stream,
28  clockType::duration t) {
29  stream << std::fixed;
30  stream << std::chrono::duration_cast<std::chrono::duration<double>>(t).count();
31  return stream;
32 }
33 
34 
35 class baseTest {
36  protected:
37  std::string name;
38  std::string description;
39  std::list<std::pair<std::string, std::string>> parameters;
40  std::list<std::pair<options::base&, std::string>> options;
41 
42  std::set<std::string> createdFiles;
43 
56  static std::string descriptions;
57  public:
58  static std::map<std::string, baseTest*>& getList() {
59  static std::remove_reference<decltype(getList())>::type list;
60  return list;
61  }
62  static void setUpOptions() {
67  }
68  baseTest(const std::string& aName,
69  const std::string& aDescription,
70  const decltype(parameters)& aParameters,
71  const decltype(options)& aOptions = {}):
72  name(aName),
73  description(aDescription),
74  parameters(aParameters),
75  options(aOptions) {
76  getList().emplace(name, this);
77 
78  descriptions += name;
79  descriptions += ": ";
81  descriptions += "\n";
82  if (!options.empty()) {
83  descriptions += "\toptions:\n";
84  for (const auto& opt : options) {
85  descriptions += "\t\t";
86  descriptions += opt.first.fGetLongName();
87  descriptions += ": ";
88  descriptions += opt.second;
89  descriptions += "\n";
90  }
91  }
92  descriptions += "\tparameters:\n";
93  for (const auto& par : parameters) {
94  descriptions += "\t\t";
95  descriptions += par.first;
96  descriptions += ": ";
97  descriptions += par.second;
98  descriptions += "\n";
99  }
100  };
101  static const std::string& getDescriptions() {
102  return descriptions;
103  }
104  virtual void execute(const std::vector<std::string>& remainingOptions) = 0;
105 
106  void ensuredWrite(int fd, const std::string& data) {
107  auto bytesToWrite = data.size();
108  auto ptr = data.data();
109  while (bytesToWrite > 0) {
110  auto result = write(fd, ptr, bytesToWrite);
111  if (result == -1) {
112  if (errno == EINTR) {
113  continue;
114  } else {
115  throwcall::badval(result, -1, "write ", bytesToWrite, " to fd ", fd);
116  }
117  } else {
118  bytesToWrite -= result;
119  }
120  }
121  }
122  void ensuredRead(int fd, std::string& data) {
123  auto bytesToRead = data.size();
124  auto ptr = const_cast<char*>(data.data()); // c++17 legalizes this...
125  while (bytesToRead > 0) {
126  auto result = read(fd, ptr, bytesToRead);
127  if (result == -1 && errno == EINTR) {
128  continue;
129  }
130  throwcall::badval(result, -1, "read ", bytesToRead, " from fd ", fd);
131  bytesToRead -= result;
132  }
133  }
134  void Execute(const std::vector<std::string>& remainingOptions) {
135 
136  if (size > 0) {
137  content.resize(size);
138  }
139 
140  if (urandom) {
141  auto fd = throwcall::badval(open("/dev/urandom", O_RDONLY), -1, "open /dev/urandom");
142  ensuredRead(fd, content);
143  throwcall::good0(close(fd), "close /dev/urandom");
144  }
145  if (einsZweiDrei) {
146  auto s = content.size();
147  content.clear();
148  content.reserve(s);
149  for (unsigned i = 0; true; i++) {
150  auto number = std::to_string(i);
151  if (content.size() + number.size() < content.capacity()) {
152  content += number;
153  if (content.size() < content.capacity()) {
154  content.push_back('\n');
155  }
156  } else {
157  break;
158  }
159  }
160  while (content.size() < content.capacity()) {
161  content.push_back('+');
162  }
163  }
164  if (!contentFile.empty()) {
165  auto fd = throwcall::badval(open(contentFile.c_str(), O_RDONLY), -1, "open ", contentFile);
166  struct stat statData;
167  throwcall::good0(fstat(fd, &statData), "stat ", contentFile);
168  content.resize(statData.st_size);
169  ensuredRead(fd, content);
170  }
171 
172  std::vector<clockType::duration> times;
173  times.reserve(repeat);
174 
175  for (unsigned i = 0; i < repeat; i++) {
176  if (syncus > 0) {
177  auto now = std::chrono::system_clock::now();
178  auto seconds = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count();
179  auto divresult = std::div(seconds, syncus);
180  auto then = std::chrono::system_clock::time_point(std::chrono::microseconds(divresult.quot * syncus + syncus + (i % syncus) * skewus));
181  std::this_thread::sleep_until(then);
182  }
183  auto tStart = clockType::now();
184  execute(remainingOptions);
185  auto tStop = clockType::now();
186  auto dt = tStop - tStart;
187  times.push_back(dt);
188  }
189  if (cleanup) {
190  for (const auto& file : createdFiles) {
191  std::cout << "removing " << file << "\n";
192  throwcall::good0(unlink(file.c_str()), "unlink ", file);
193  }
194  }
195 
196  if (repeat == 1) {
197  std::cout << "took " << times.at(0) << "s\n";
198  } else {
199  auto minMax = std::minmax_element(times.begin(), times.end());
200  std::cout << "minimum time: " << *(minMax.first) << "s\n";
201  std::cout << "maximum time: " << *(minMax.second) << "s\n";
202  auto sum = std::accumulate(times.begin(), times.end(), clockType::duration::zero());
203  auto mean = std::chrono::duration_cast<std::chrono::duration<double>>(sum).count() / repeat;
204  std::cout << "average time: " << mean << "s\n";
205  std::map<double, unsigned long> histogram;
206  unsigned bins = 10;
207  auto range = std::chrono::duration_cast<std::chrono::duration<double>>(*(minMax.second) - * (minMax.first)).count();
208  auto lowBorder = std::chrono::duration_cast<std::chrono::duration<double>>(*(minMax.first)).count();
209  if (mean < lowBorder + range / 10) { // use log bins
210  auto border = lowBorder;
211  histogram[border] = 0;
212  auto factor = std::pow(std::chrono::duration_cast<std::chrono::duration<double>>(*(minMax.second)).count() / lowBorder,
213  1.0 / bins);
214  for (unsigned i = 0; i < bins; i++) {
215  border *= factor;
216  histogram[border] = 0;
217  }
218  } else { // use lin bins
219  lowBorder -= range / (2 * bins);
220  if (lowBorder < 0) {
221  lowBorder = 0;
222  }
223  for (unsigned i = 0; i < bins + 1; i++) {
224  histogram[lowBorder + i * range / bins] = 0;
225  }
226  }
227  for (auto t : times) {
228  auto bin = histogram.begin();
229  for (auto testBin = bin; testBin != histogram.end(); ++testBin) {
230  if (std::chrono::duration_cast<std::chrono::duration<double>>(t).count() < testBin->first) {
231  break;
232  }
233  bin = testBin;
234  }
235  bin->second++;
236  }
237  for (auto& bin : histogram) {
238  std::cout << bin.first << ": " << bin.second << "\n";
239  }
240  }
241  }
242 };
243 std::string baseTest::descriptions = "\npossible actions and their parameters:\n";
245  "repeat this many times", 1);
247  "remove created files after running tests");
249  "get content from /dev/urandom");
251  "set content to decimal ascenfing numbers");
253  "name of file for content", "");
255  "size of content string", 0);
257  "number of blocks to write", 1);
258 
260  "content of file", "blabla");
262  "use O_TRUNC on open()");
264  "characteristic test time (s)",
265  std::chrono::seconds(0));
267  "sync start times to sync microseconds", 0);
269  "skew start times by skew microseconds", 0);
270 
271 class tmpFile: public baseTest {
272  public:
273  tmpFile(): baseTest("tmpFile",
274  "create a file via O_TMPFILE", {
275  {"dir", "directory to create the file in"},
276  {"file", "path to the file to create"}
277  }) {};
278  void execute(const std::vector<std::string>& remainingOptions) override {
279  auto dir = remainingOptions.at(0);
280  auto file = remainingOptions.at(1);
281  auto result = unlink(file.c_str());
282  if (result == -1 && errno != ENOENT) {
283  throwcall::good0(result, "unlink ", file);
284  }
285  auto fd = throwcall::badval(open(dir.c_str(), O_TMPFILE | O_WRONLY, 0777), -1,
286  "open anonymous file in ", dir);
287  ensuredWrite(fd, content);
288  std::string path("/proc/self/fd/");
289  path += std::to_string(fd);
290  throwcall::good0(linkat(AT_FDCWD, path.c_str(), AT_FDCWD, file.c_str(), AT_SYMLINK_FOLLOW),
291  "link ", path, " to ", file);
292  throwcall::good0(close(fd), "close ", file);
293  createdFiles.emplace(file);
294  }
295 };
297 
298 class vimWrite: public baseTest {
299  public:
300  vimWrite(): baseTest("vimWrite",
301  "simulate the way vim writes a file", {
302  {"file", "path to the file to create"
303  }
304  }) {};
305 
306  void execute(const std::vector<std::string>& remainingOptions) override {
307  auto file = remainingOptions.at(0);
308  std::string tildeFile(file);
309  tildeFile += "~";
310  struct stat buf;
311  unlink(tildeFile.c_str());
312  if (stat(file.c_str(), &buf) != 0) {
313  if (errno == ENOENT) {
314  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644), -1,
315  "open ", file);
316  ensuredWrite(fd, "this is the old file...\n");
317  ensuredWrite(fd, content);
318  ensuredWrite(fd, "this is the old file...\n");
319  throwcall::good0(close(fd), "close ", file);
320  }
321  }
322  throwcall::good0(rename(file.c_str(), tildeFile.c_str()),
323  "rename ", file, " to ", tildeFile);
324  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644), -1,
325  "open ", file);
326  ensuredWrite(fd, content);
327  throwcall::good0(fsync(fd), "sync ", file);
328  throwcall::good0(close(fd), "close ", file);
329  throwcall::good0(unlink(tildeFile.c_str()), "unlink ", tildeFile);
330  createdFiles.emplace(file);
331  }
332 };
334 
335 
336 class keepOpen: public baseTest {
337  public:
338  keepOpen(): baseTest("keepOpen",
339  "open file for reading and keep open", {
340  {"file", "path to file"
341  }
342  },
343  {{time, "keep open this long"}}) {};
344  void execute(const std::vector<std::string>& remainingOptions) override {
345  auto file = remainingOptions.at(0);
346  auto fd = throwcall::badval(open(file.c_str(), O_RDONLY), -1, "open ", file);
347  std::this_thread::sleep_for(time);
348  throwcall::good0(close(fd), "close ", file);
349  }
350 };
352 
353 
354 class appendBlocks: public baseTest {
355  public:
356  appendBlocks(): baseTest("appendBlocks",
357  "create a file and append blocks to it, changing the first one.", {
358  {"file", "path to file"
359  }
360  },
361  {{time, "wait this long between writes"}}) {};
362  void execute(const std::vector<std::string>& remainingOptions) override {
363  auto file = remainingOptions.at(0);
364  for (unsigned i = 0; i < blocks; i++) {
365  if (i > 0) {
366  std::this_thread::sleep_for(time);
367  }
368  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1, "open ", file);
369  throwcall::badval(lseek(fd, 0, SEEK_END), -1, "seek to end of ", file);
370  ensuredWrite(fd, content);
371  throwcall::badval(lseek(fd, 0, SEEK_SET), -1, "seek to start of ", file);
372  ensuredWrite(fd, std::to_string(i));
373  throwcall::good0(close(fd), "close ", file);
374  }
375  createdFiles.emplace(file);
376  };
377 
378 };
380 
381 class simpleWrite: public baseTest {
382  public:
383  simpleWrite(): baseTest("simpleWrite",
384  "create a file and write into it", {
385  {"file", "path to file"
386  }
387  },
388  {{time, "wait this long between writes"},
389  {truncate, "use O_TRUNC on open()"}}) {};
390  void execute(const std::vector<std::string>& remainingOptions) override {
391  auto file = remainingOptions.at(0);
392  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY | ( truncate ? O_TRUNC : 0), 0744), -1, "open ", file);
393  for (unsigned i = 0; i < blocks; i++) {
394  if (i > 0) {
395  std::this_thread::sleep_for(time);
396  }
397  ensuredWrite(fd, content);
398  }
399  throwcall::good0(close(fd), "close ", file);
400  createdFiles.emplace(file);
401  };
402 };
404 
405 
406 class cpWrite: public baseTest {
407  public:
408  cpWrite(): baseTest("cpWrite",
409  "create a file and write into it like cp does", {
410  {"file", "path to file"
411  }
412  },
413  {{time, "wait this long between writes"}}) {};
414  void execute(const std::vector<std::string>& remainingOptions) override {
415  auto file = remainingOptions.at(0);
416  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY | O_EXCL, 0700), -1, "open ", file);
417  for (unsigned i = 0; i < blocks; i++) {
418  if (i > 0) {
419  std::this_thread::sleep_for(time);
420  }
421  ensuredWrite(fd, content);
422  }
423  throwcall::good0(fchmod(fd,0755), "fchmod ", file);
424  throwcall::good0(close(fd), "close ", file);
425  createdFiles.emplace(file);
426  };
427 };
429 
430 
431 class directWrite: public baseTest {
432  public:
433  directWrite(): baseTest("directWrite",
434  "create a file with O_DIRECT and write into it", {
435  {"file", "path to file"
436  }
437  },
438  {{time, "wait this long between writes"}}) {};
439  void execute(const std::vector<std::string>& remainingOptions) override {
440  auto file = remainingOptions.at(0);
441  // we need a page aligned buffer
442  auto buffer = throwcall::badval(mmap(nullptr, content.size(),
443  PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS,
444  -1, 0),
445  MAP_FAILED,
446  "mmap ", content.size(), " bytes anonymously");
447  memcpy(buffer, content.data(), content.size());
448  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY | O_DIRECT, 0744), -1, "open ", file);
449  for (unsigned i = 0; i < blocks; i++) {
450  if (i > 0) {
451  std::this_thread::sleep_for(time);
452  }
453  throwcall::good0(write(fd, buffer, content.size()) - content.size(), "write");
454  }
455  throwcall::good0(close(fd), "close ", file);
456  throwcall::good0(munmap(buffer, content.size()), "munmap buffer");
457  createdFiles.emplace(file);
458  };
459 };
461 
462 class mmapWrite: public baseTest {
463  public:
464  mmapWrite(): baseTest("mmapWrite",
465  "create a file, mmap() it and write into it", {
466  {"file", "path to file"
467  }
468  }) {};
469  void execute(const std::vector<std::string>& remainingOptions) override {
470  auto file = remainingOptions.at(0);
471  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_RDWR, 0744), -1, "open ", file);
472  auto mapSize = content.size() * blocks;
473  throwcall::good0(ftruncate(fd, mapSize), "truncate to ", mapSize, " bytes");
474  auto map = throwcall::badval(mmap(nullptr, mapSize,
475  PROT_WRITE, MAP_SHARED,
476  fd, 0),
477  MAP_FAILED,
478  "mmap ", content.size(), " bytes from ", file);
479  for (unsigned i = 0; i < blocks; i++) {
480  memcpy(static_cast<char*>(map) + content.size()*i, content.data(), content.size());
481  }
482  throwcall::good0(munmap(map, mapSize), "munmap");
483  throwcall::good0(close(fd), "close ", file);
484  createdFiles.emplace(file);
485  };
486 };
488 
489 class sparseWrite: public baseTest {
490  public:
491  sparseWrite(): baseTest("sparseWrite",
492  "create a sparse file, data (hole data){blocks}", {
493  {"file", "path to file"
494  }
495  }) {};
496  void execute(const std::vector<std::string>& remainingOptions) override {
497  auto file = remainingOptions.at(0);
498  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1, "open ", file);
499  ensuredWrite(fd, content);
500  for (unsigned i = 0; i < blocks; i++) {
501  throwcall::badval(lseek(fd, content.size(), SEEK_CUR), -1,
502  "lseek in ", file, " for ", content.size(), " bytes");
503  ensuredWrite(fd, content);
504  }
505  throwcall::good0(close(fd), "close ", file);
506  createdFiles.emplace(file);
507  };
508 };
510 
511 class sparseWrite2: public baseTest {
512  public:
513  sparseWrite2(): baseTest("sparseWrite2",
514  "create a sparse file, hole (data hole){blocks}", {
515  {"file", "path to file"
516  }
517  }) {};
518  void execute(const std::vector<std::string>& remainingOptions) override {
519  auto file = remainingOptions.at(0);
520  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1, "open ", file);
521  throwcall::badval(lseek(fd, content.size(), SEEK_CUR), -1,
522  "lseek in ", file, " for ", content.size(), " bytes");
523  for (unsigned i = 0; i < blocks; i++) {
524  ensuredWrite(fd, content);
525  throwcall::good0(ftruncate(fd, lseek(fd, 0, SEEK_CUR) + content.size()), "truncate ", file);
526  throwcall::badval(lseek(fd, content.size(), SEEK_CUR), -1,
527  "lseek in ", file, " for ", content.size(), " bytes");
528  }
529  throwcall::good0(close(fd), "close ", file);
530  createdFiles.emplace(file);
531  };
532 };
534 
535 
536 
537 class findMinHoleSize: public baseTest {
538  public:
539  findMinHoleSize(): baseTest("findMinHoleSize",
540  "find minimum hole size", {
541  {"file", "path to file"
542  }
543  }) {};
544  void execute(const std::vector<std::string>& remainingOptions) override {
545  auto file = remainingOptions.at(0);
546  auto fd = throwcall::badval(open(file.c_str(), O_CREAT | O_WRONLY, 0744), -1, "open ", file);
547  struct stat statbuf;
548  throwcall::good0(fstat(fd, &statbuf), "stat ", file);
549  auto testSize = statbuf.st_blksize;
550  while (testSize > 0) {
551  throwcall::good0(close(fd), "close ", file);
552  fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_TRUNC, 0744), -1, "open ", file);
553  throwcall::badval(lseek(fd, testSize, SEEK_SET), -1, "seek ", testSize, " in ", file);
554  ensuredWrite(fd, content);
555  throwcall::good0(close(fd), "close ", file);
556  createdFiles.emplace(file);
557  fd = throwcall::badval(open(file.c_str(), O_RDONLY), -1, "open ", file);
558  auto dataPos = throwcall::badval(lseek(fd, 0, SEEK_DATA), -1, "seek data in ", file);
559  if (dataPos != testSize) {
560  break;
561  }
562  std::cout << testSize << " bytes hole size worked\n";
563  testSize /= 2;
564  }
565  };
566 };
568 
569 
570 class appendHole: public baseTest {
571  public:
572  appendHole(): baseTest("appendHole",
573  "append a hole to an existing file", {
574  {"file", "path to file"
575  }
576  }) {};
577  void execute(const std::vector<std::string>& remainingOptions) override {
578  auto file = remainingOptions.at(0);
579  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY, 0744), -1, "open ", file);
580  auto oldFileEnd = throwcall::badval(lseek(fd, 0, SEEK_END), -1, "seek end of ", file);
581  throwcall::good0(ftruncate(fd, oldFileEnd + content.size() * blocks), "truncate ", file);
582  throwcall::good0(close(fd), "close ", file);
583  createdFiles.emplace(file);
584  };
585 };
587 
588 
590  public:
591  createUnlinkCreate(): baseTest("createUnlink",
592  "create, unlink, re-create a file", {
593  {"file", "path to file"
594  }
595  }) {};
596  void execute(const std::vector<std::string>& remainingOptions) override {
597  auto file = remainingOptions.at(0);
598  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1, "open ", file);
599  ensuredWrite(fd, "fist creation (before unlink)\n");
600  ensuredWrite(fd, content);
601  throwcall::good0(close(fd), "close ", file);
602  throwcall::good0(unlink(file.c_str()), "unlink ", file);
603  fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1, "open ", file);
604  ensuredWrite(fd, "second creation (after unlink)\n");
605  ensuredWrite(fd, content);
606  throwcall::good0(close(fd), "close ", file);
607  createdFiles.emplace(file);
608  };
609 };
611 
612 class closeMvUpdate: public baseTest {
613  public:
614  closeMvUpdate(): baseTest("closeMvUpdate",
615  "close, move, update a file", {
616  {"file", "path to file"
617  }
618  }) {};
619  void execute(const std::vector<std::string>& remainingOptions) override {
620  auto file = remainingOptions.at(0);
621  std::string movedFile(file);
622  movedFile += ".moved";
623  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1, "open ", file);
624  ensuredWrite(fd, content);
625  throwcall::good0(close(fd), "close ", file);
626  throwcall::good0(rename(file.c_str(), movedFile.c_str()),
627  "rename ", file, " to ", movedFile);
628  struct timespec times[2] = {{0, UTIME_OMIT}, {0, UTIME_NOW}};
629  throwcall::good0(utimensat(AT_FDCWD, movedFile.c_str(), times, 0),
630  "change mtime of ", file);
631  createdFiles.emplace(movedFile);
632  };
633 };
635 
637  public:
638  createUnlinkClose(): baseTest("createUnlinkClose",
639  "create, unlink, update a file", {
640  {"file", "path to file"
641  }
642  }) {};
643  void execute(const std::vector<std::string>& remainingOptions) override {
644  auto file = remainingOptions.at(0);
645  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1, "open ", file);
646  throwcall::good0(unlink(file.c_str()), "unlink ", file);
647  ensuredWrite(fd, content);
648  throwcall::good0(close(fd), "close ", file);
649  };
650 };
652 
653 class simpleRead: public baseTest {
654  public:
655  simpleRead(): baseTest("simpleRead",
656  "read file and compare to content", {
657  {"file", "path to file"
658  }
659  }) {};
660  void execute(const std::vector<std::string>& remainingOptions) override {
661  auto file = remainingOptions.at(0);
662  auto fd = throwcall::badval(open(file.c_str(), O_RDONLY), -1, "open ", file);
663  std::string data(content.size(), '\0');
664  ensuredRead(fd, data);
665  if (data.compare(content) != 0) {
666  std::cout << "difference betwen content and file found\n";
667  }
668  throwcall::good0(close(fd), "close ", file);
669  };
670 };
672 
673 class mmapRead: public baseTest {
674  public:
675  mmapRead(): baseTest("mmapRead",
676  "read file via mmap() and compare to content", {
677  {"file", "path to file"
678  }
679  }) {};
680  void execute(const std::vector<std::string>& remainingOptions) override {
681  auto file = remainingOptions.at(0);
682  auto fd = throwcall::badval(open(file.c_str(), O_RDONLY), -1, "open ", file);
683  auto map = throwcall::badval(mmap(nullptr, content.size(),
684  PROT_READ, MAP_PRIVATE | MAP_POPULATE,
685  fd, 0),
686  MAP_FAILED,
687  "mmap ", content.size(), " bytes from ", file);
688  auto data = static_cast<const char *>(map);
689  if (content.compare(0, content.size(), data, content.size()) != 0) {
690  std::cout << "difference betwen content and file found\n";
691  }
692  throwcall::good0(munmap(map, content.size()), "unmap ", file);
693  throwcall::good0(close(fd), "close ", file);
694  };
695 };
697 
698 
699 class truncateToSize: public baseTest {
700  public:
701  truncateToSize(): baseTest("truncateToSize",
702  "truncate existing file to it;s size", {
703  {"file", "path to file"
704  }
705  }) {};
706  void execute(const std::vector<std::string>& remainingOptions) override {
707  auto file = remainingOptions.at(0);
708  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY), -1, "open ", file);
709  struct stat statbuf;
710  throwcall::good0(fstat(fd, &statbuf), "stat ", file);
711  throwcall::good0(ftruncate(fd, statbuf.st_size), "truncate ", file, " to ", statbuf.st_size, " bytes");
712  throwcall::good0(close(fd), "close ", file);
713  };
714 };
716 
717 #ifdef withGpfs
718 class gpfsPrealloc: public baseTest {
719  public:
720  gpfsPrealloc(): baseTest("gpfsPrealloc",
721  "write a file with gpfsRealloc", {
722  {"file", "path to file"
723  }
724  }) {};
725  void execute(const std::vector<std::string>& remainingOptions) override {
726  auto file = remainingOptions.at(0);
727  auto fd = throwcall::badval(open(file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744), -1, "open ", file);
728  throwcall::good0(gpfs_prealloc(fd, 0, content.size()),
729  "gpfs_prealloc ", content.size(), " bytes for ", file);
730  ensuredWrite(fd, content);
731  throwcall::good0(close(fd), "close ", file);
732  };
733 };
734 static gpfsPrealloc gpfsPreallocInstance;
735 #endif
736 
737 class uglyNames: public baseTest {
738  public:
739  uglyNames(): baseTest("uglyNames",
740  "write files with ugly names", {
741  {"dir", "directory to create the files in"
742  }
743  }) {};
744  void execute(const std::vector<std::string>& remainingOptions) override {
745  auto dir = remainingOptions.at(0);
746  auto dirfd = throwcall::badval(open(dir.c_str(),O_PATH | O_DIRECTORY),-1,"can't open ",dir);
747  std::list<std::string> names{"name with spaces",
748  "name\nwith\nnewlines",
749  "name\rwith\rreturns",
750  "name\r\nwith\r\nCRLF",
751  "name_with_\033[91mred\033[0m_part",
752  "name_with_\xe2\x80\xadleft-to-right\xe2\x80\x8e_part",
753  "name_with_\xe2\x82\xac_euro",
754  "name_with_ɄɳɨȻʘΔε_unicode",
755  "name_with_🤔💩☢_emojis",
756  "name_with_䭫顙_more_unicode"
757  };
758  {
759  std::list<std::pair<std::string,unsigned char>> ControlChars{
760  {"SOH", 001}, {"STX", 002}, {"ETX", 003}, {"EOT", 004},
761  {"ENQ", 005}, {"ACK", 006},
762  {"BEL", 007},
763  {"BS", 010},
764  {"HT", 011},
765  {"LF", 012},
766  {"VT", 013},
767  {"FF", 014},
768  {"CR", 015},
769  {"SO", 016},
770  {"SI", 017},
771  {"DLE", 020},
772  {"DC1", 021},
773  {"DC2", 022},
774  {"DC3", 023},
775  {"DC4", 024},
776  {"NAK", 025},
777  {"SYN", 026},
778  {"ETB", 027},
779  {"CAN", 030},
780  {"EM", 031},
781 {"SUB", 032},
782 {"ESC", 033},
783 {"FS", 034},
784 {"GS", 035},
785 {"RS", 036},
786 {"US", 037},
787 {"DEL",0177}
788  };
789  for (const auto& cc: ControlChars) {
790  std::string ugly("name_with_");
791  ugly.push_back(cc.second);
792  ugly+="_char_";
793  ugly+=std::to_string(cc.second);
794  ugly+="_";
795  ugly+=cc.first;
796  names.push_back(ugly);
797  }
798  }
799  {
800  std::vector<std::pair<unsigned char, unsigned char>> ranges{{32,46},{58,63},{91,96},{123,126}};
801  for (const auto& range: ranges) {
802  for (unsigned char c=range.first; c<=range.second; c++) {
803  std::string ugly("name_with_");
804  ugly.push_back(c);
805  ugly+="_char_";
806  ugly+=std::to_string(c);
807  names.push_back(ugly);
808  }
809  }
810  }
811  for (unsigned char c=128; c!=0; c++) {
812  std::string ugly("name_with_");
813  ugly.push_back(c);
814  ugly+="_char_";
815  ugly+=std::to_string(c);
816  names.push_back(ugly);
817  }
818  {
819  auto nameMax = throwcall::badval(fpathconf(dirfd,_PC_NAME_MAX),-1,"can't get max name length");
820  auto ugly = std::to_string(nameMax);
821  ugly.resize(nameMax,'b');
822  names.push_back(ugly);
823  }
824 
825  for (const auto& ugly: names) {
826  std::string file(dir);
827  file+="/";
828  file+=ugly;
829  auto fd = openat(dirfd,ugly.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744);
830  if (fd == -1 && errno == EINVAL) {
831  std::cerr << "illegal file name \" << ugly << \" tried: ";
832  for (auto c : ugly) {
833  std::cerr << std::hex << std::setw(2) << c;
834  }
835  std::cerr << std::dec << "\n";
836  continue;
837  } else {
838  throwcall::badval(fd,-1, "open ", file);
839  }
840  ensuredWrite(fd, ugly);
841  throwcall::good0(close(fd), "close ", file);
842  createdFiles.emplace(file);
843  }
844  };
845 };
847 
848 class uglyPaths: public baseTest {
849  public:
850  uglyPaths(): baseTest("uglyPaths",
851  "write files in paths with ugly names", {
852  {"dir", "directory to create the paths in"
853  }
854  }) {};
855  void execute(const std::vector<std::string>& remainingOptions) override {
856  auto dir = remainingOptions.at(0);
857  auto dirfd = throwcall::badval(open(dir.c_str(),O_PATH | O_DIRECTORY),-1,"can't open ",dir);
858  std::list<std::string> names{"path with spaces",
859  "path\nwith\nnewlines",
860  "path\rwith\rreturns",
861  "path\r\nwith\r\nCRLF",
862  "path_with_\033[91mred\033[0m_part",
863  "path_with_\xe2\x80\xadleft-to-right\xe2\x80\x8e_part",
864  "path_with_\xe2\x82\xac_euro",
865  "path_with_ɄɳɨȻʘΔε_unicode",
866  "path_with_🤔💩☢_emojis",
867  "path_with_䭫顙_more_unicode"
868  };
869  {
870  auto nameMax = throwcall::badval(fpathconf(dirfd,_PC_NAME_MAX),-1,"can't get max name length");
871  auto ugly = std::to_string(nameMax);
872  ugly.resize(nameMax,'b');
873  names.push_back(ugly);
874  }
875 
876  for (const auto& ugly: names) {
877  auto retval = mkdirat(dirfd,ugly.c_str(), 0777);
878  if (retval == -1 && errno == EINVAL) {
879  std::cerr << "illegal dir name \" << ugly << \" tried \n";
880  continue;
881  } else {
882  throwcall::badval(retval,-1, "mkdir ", ugly);
883  }
884  std::string file(ugly);
885  file += "/file";
886  auto fd = throwcall::badval(openat(dirfd, file.c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0744),
887  -1, "open ", file);
888  ensuredWrite(fd, ugly);
889  throwcall::good0(close(fd), "close ", file);
890  createdFiles.emplace(dir + "/" + file);
891  }
892  };
893 };
895 
896 
897 int main(int argc, const char *argv[]) {
898  options::parser parser("test utility for strange file operations",
901  options::positional<options::single<std::string>> action(10, "action", "test to execute",
902  "");
903  parser.fRequire(&action);
904  for (const auto& test : baseTest::getList()) {
905  action.fAddToRange(test.first);
906  }
907  auto remainingOptions = parser.fParse(argc, argv);
908 
909  auto test = baseTest::getList().find(action);
910  if (test != baseTest::getList().end()) {
911  test->second->Execute(remainingOptions);
912  } else {
913  std::cerr << "unknown action '" << action << "'\n";
914  return EXIT_FAILURE;
915  }
916  return EXIT_SUCCESS;
917 }
baseTest::time
static options::single< std::chrono::duration< double > > time
Definition: fileOpsTests.cpp:53
baseTest::options
std::list< std::pair< options::base &, std::string > > options
Definition: fileOpsTests.cpp:40
findMinHoleSize
Definition: fileOpsTests.cpp:537
sparseWrite2
Definition: fileOpsTests.cpp:511
closeMvUpdate::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:619
mmapWrite
Definition: fileOpsTests.cpp:462
options::parser
class that contains the parser, i.e. does that option handling
Definition: Options.h:363
options::single< unsigned int >
simpleWrite
Definition: fileOpsTests.cpp:381
baseTest::name
std::string name
Definition: fileOpsTests.cpp:37
cpWrite
Definition: fileOpsTests.cpp:406
simpleWrite::simpleWrite
simpleWrite()
Definition: fileOpsTests.cpp:383
uglyNamesInstance
static uglyNames uglyNamesInstance
Definition: fileOpsTests.cpp:846
keepOpen
Definition: fileOpsTests.cpp:336
baseTest::content
static options::single< std::string > content
Definition: fileOpsTests.cpp:51
main
int main(int argc, const char *argv[])
Definition: fileOpsTests.cpp:897
truncateToSize::truncateToSize
truncateToSize()
Definition: fileOpsTests.cpp:701
options::single< bool >
class specialisation for options of type bool
Definition: Options.h:595
vimWrite::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:306
throwcall::badval
T badval(T call, t badvalue, const Args &... args)
template function to wrap system calls that return a special bad value on failure
Definition: throwcall.h:54
baseTest::einsZweiDrei
static options::single< bool > einsZweiDrei
Definition: fileOpsTests.cpp:47
options::parser::fParse
const std::vector< std::string > & fParse(int argc, const char *argv[])
parse the options on the command line
Definition: Options.cpp:168
closeMvUpdateInstance
static closeMvUpdate closeMvUpdateInstance
Definition: fileOpsTests.cpp:634
createUnlinkCreateInstance
static createUnlinkCreate createUnlinkCreateInstance
Definition: fileOpsTests.cpp:610
mmapReadInstance
static mmapRead mmapReadInstance
Definition: fileOpsTests.cpp:696
sparseWrite::sparseWrite
sparseWrite()
Definition: fileOpsTests.cpp:491
appendHole::appendHole
appendHole()
Definition: fileOpsTests.cpp:572
baseTest::ensuredRead
void ensuredRead(int fd, std::string &data)
Definition: fileOpsTests.cpp:122
baseTest::contentFile
static options::single< std::string > contentFile
Definition: fileOpsTests.cpp:48
appendBlocks
Definition: fileOpsTests.cpp:354
baseTest::skewus
static options::single< unsigned long > skewus
Definition: fileOpsTests.cpp:55
simpleRead::simpleRead
simpleRead()
Definition: fileOpsTests.cpp:655
mmapRead
Definition: fileOpsTests.cpp:673
keepOpen::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:344
simpleRead
Definition: fileOpsTests.cpp:653
cpWrite::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:414
truncateToSizeInstance
static truncateToSize truncateToSizeInstance
Definition: fileOpsTests.cpp:715
tmpFile::tmpFile
tmpFile()
Definition: fileOpsTests.cpp:273
createUnlinkCreate
Definition: fileOpsTests.cpp:589
appendBlocks::appendBlocks
appendBlocks()
Definition: fileOpsTests.cpp:356
cpWriteInstance
static cpWrite cpWriteInstance
Definition: fileOpsTests.cpp:428
simpleRead::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:660
mmapRead::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:680
createUnlinkClose
Definition: fileOpsTests.cpp:636
uglyNames
Definition: fileOpsTests.cpp:737
baseTest::Execute
void Execute(const std::vector< std::string > &remainingOptions)
Definition: fileOpsTests.cpp:134
appendHoleInstance
static appendHole appendHoleInstance
Definition: fileOpsTests.cpp:586
baseTest::createdFiles
std::set< std::string > createdFiles
Definition: fileOpsTests.cpp:42
appendHole::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:577
truncateToSize::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:706
directWrite::directWrite
directWrite()
Definition: fileOpsTests.cpp:433
options
Definition: Options.h:33
tmpFile::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:278
tmpFileInstance
static tmpFile tmpFileInstance
Definition: fileOpsTests.cpp:296
baseTest::truncate
static options::single< bool > truncate
Definition: fileOpsTests.cpp:52
baseTest::getList
static std::map< std::string, baseTest * > & getList()
Definition: fileOpsTests.cpp:58
vimWriteInstance
static vimWrite vimWriteInstance
Definition: fileOpsTests.cpp:333
sparseWrite2::sparseWrite2
sparseWrite2()
Definition: fileOpsTests.cpp:513
simpleWriteInstance
static simpleWrite simpleWriteInstance
Definition: fileOpsTests.cpp:403
mmapWrite::mmapWrite
mmapWrite()
Definition: fileOpsTests.cpp:464
options::base::fForbid
virtual void fForbid(const base *aOtherOption)
forbid aOtherOption when this option is set
Definition: Options.cpp:617
baseTest::description
std::string description
Definition: fileOpsTests.cpp:38
simpleReadInstance
static simpleRead simpleReadInstance
Definition: fileOpsTests.cpp:671
cpWrite::cpWrite
cpWrite()
Definition: fileOpsTests.cpp:408
baseTest::execute
virtual void execute(const std::vector< std::string > &remainingOptions)=0
uglyPaths::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:855
directWrite::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:439
baseTest
Definition: fileOpsTests.cpp:35
vimWrite
Definition: fileOpsTests.cpp:298
OptionsChrono.h
baseTest::urandom
static options::single< bool > urandom
Definition: fileOpsTests.cpp:46
operator<<
std::ostream & operator<<(std::ostream &stream, clockType::duration t)
Definition: fileOpsTests.cpp:27
throwcall.h
sparseWriteInstance2
static sparseWrite2 sparseWriteInstance2
Definition: fileOpsTests.cpp:533
truncateToSize
Definition: fileOpsTests.cpp:699
findMinHoleSize::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:544
baseTest::repeat
static options::single< unsigned int > repeat
Definition: fileOpsTests.cpp:44
directWriteInstance
static directWrite directWriteInstance
Definition: fileOpsTests.cpp:460
baseTest::size
static options::single< size_t > size
Definition: fileOpsTests.cpp:49
uglyNames::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:744
createUnlinkCreate::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:596
vimWrite::vimWrite
vimWrite()
Definition: fileOpsTests.cpp:300
appendHole
Definition: fileOpsTests.cpp:570
directWrite
Definition: fileOpsTests.cpp:431
mmapRead::mmapRead
mmapRead()
Definition: fileOpsTests.cpp:675
findMinHoleSizeInstance
static findMinHoleSize findMinHoleSizeInstance
Definition: fileOpsTests.cpp:567
mmapWriteInstance
static mmapWrite mmapWriteInstance
Definition: fileOpsTests.cpp:487
appendBlocksInstance
static appendBlocks appendBlocksInstance
Definition: fileOpsTests.cpp:379
keepOpen::keepOpen
keepOpen()
Definition: fileOpsTests.cpp:338
baseTest::setUpOptions
static void setUpOptions()
Definition: fileOpsTests.cpp:62
uglyPaths::uglyPaths
uglyPaths()
Definition: fileOpsTests.cpp:850
closeMvUpdate
Definition: fileOpsTests.cpp:612
tmpFile
Definition: fileOpsTests.cpp:271
baseTest::descriptions
static std::string descriptions
Definition: fileOpsTests.cpp:56
appendBlocks::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:362
createUnlinkCreate::createUnlinkCreate
createUnlinkCreate()
Definition: fileOpsTests.cpp:591
clockType
std::chrono::high_resolution_clock clockType
Definition: fileOpsTests.cpp:26
findMinHoleSize::findMinHoleSize
findMinHoleSize()
Definition: fileOpsTests.cpp:539
options::positional
Definition: Options.h:876
sparseWrite2::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:518
createUnlinkClose::createUnlinkClose
createUnlinkClose()
Definition: fileOpsTests.cpp:638
sparseWrite::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:496
createUnlinkCloseInstance
static createUnlinkClose createUnlinkCloseInstance
Definition: fileOpsTests.cpp:651
mmapWrite::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:469
baseTest::syncus
static options::single< unsigned long > syncus
Definition: fileOpsTests.cpp:54
uglyPathsInstance
static uglyPaths uglyPathsInstance
Definition: fileOpsTests.cpp:894
baseTest::blocks
static options::single< unsigned int > blocks
Definition: fileOpsTests.cpp:50
baseTest::baseTest
baseTest(const std::string &aName, const std::string &aDescription, const decltype(parameters)&aParameters, const decltype(options)&aOptions={})
Definition: fileOpsTests.cpp:68
closeMvUpdate::closeMvUpdate
closeMvUpdate()
Definition: fileOpsTests.cpp:614
throwcall::good0
void good0(T call, const Args &... args)
template function to wrap system calls that return 0 on success
Definition: throwcall.h:40
sparseWrite
Definition: fileOpsTests.cpp:489
baseTest::getDescriptions
static const std::string & getDescriptions()
Definition: fileOpsTests.cpp:101
baseTest::cleanup
static options::single< bool > cleanup
Definition: fileOpsTests.cpp:45
uglyNames::uglyNames
uglyNames()
Definition: fileOpsTests.cpp:739
createUnlinkClose::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:643
keepOpenInstance
static keepOpen keepOpenInstance
Definition: fileOpsTests.cpp:351
uglyPaths
Definition: fileOpsTests.cpp:848
sparseWriteInstance
static sparseWrite sparseWriteInstance
Definition: fileOpsTests.cpp:509
baseTest::parameters
std::list< std::pair< std::string, std::string > > parameters
Definition: fileOpsTests.cpp:39
simpleWrite::execute
void execute(const std::vector< std::string > &remainingOptions) override
Definition: fileOpsTests.cpp:390
baseTest::ensuredWrite
void ensuredWrite(int fd, const std::string &data)
Definition: fileOpsTests.cpp:106
options::parser::fRequire
virtual void fRequire(const base *aOtherOption)
Definition: Options.cpp:135