pstore2
file.hpp
Go to the documentation of this file.
1 //===- include/pstore/os/file.hpp -------------------------*- mode: C++ -*-===//
2 //* __ _ _ *
3 //* / _(_) | ___ *
4 //* | |_| | |/ _ \ *
5 //* | _| | | __/ *
6 //* |_| |_|_|\___| *
7 //* *
8 //===----------------------------------------------------------------------===//
9 //
10 // Part of the pstore project, under the Apache License v2.0 with LLVM Exceptions.
11 // See https://github.com/SNSystems/pstore/blob/master/LICENSE.txt for license
12 // information.
13 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
14 //
15 //===----------------------------------------------------------------------===//
18 
19 #ifndef PSTORE_OS_FILE_HPP
20 #define PSTORE_OS_FILE_HPP
21 
22 #include <cstdint>
23 #include <ctime>
24 #include <functional>
25 
26 #ifdef _WIN32
27 # define NOMINMAX
28 # define WIN32_LEAN_AND_MEAN
29 # include <Windows.h>
30 #endif
31 
32 #include "pstore/adt/error_or.hpp"
33 #include "pstore/support/array_elements.hpp"
34 #include "pstore/support/error.hpp"
35 
36 namespace pstore {
37 
38  class memory_mapper;
39  class in_memory_mapper;
40 
43  namespace file {
44  namespace details {
45 
46  // name from template
47  // ~~~~~~~~~~~~~~~~~~
71  template <typename RandomGenerator>
72  std::string name_from_template (std::string const & tmpl, RandomGenerator rng) {
73  // Walk backwards looking for the start of the sequence of 'X'
74  // characters that form the end of the template string.
75  auto const it = std::find_if (tmpl.rbegin (), tmpl.rend (),
76  [] (char const c) { return c != 'X'; });
77  // Build the result string starting from the non-template characters.
78  std::string path;
79  path.reserve (tmpl.length ());
80  std::copy (tmpl.begin (), it.base (), std::back_inserter (path));
81 
82  // Replace the sequence of 'X's with random characters.
83  auto num_x = std::distance (it.base (), tmpl.end ());
84  while (num_x--) {
85  static char const alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789_";
86  static constexpr auto length =
87  static_cast<unsigned> (array_elements (alphabet)) - 1U;
88  auto const index = rng (length);
89  PSTORE_ASSERT (index >= 0 && index < length);
90  path.push_back (alphabet[index]);
91  }
92  PSTORE_ASSERT (path.length () == tmpl.length ());
93  return path;
94  }
95 
96  // split
97  // ~~~~~
118  template <typename WidthType, typename PointeeType, typename Function>
119  std::size_t split (PointeeType * buffer, std::size_t size, Function const & function) {
120  PSTORE_ASSERT (buffer != nullptr);
121 
122  static_assert (sizeof (PointeeType) == sizeof (std::uint8_t),
123  "PointeeType must be a byte wide type");
124  static_assert (sizeof (std::size_t) >= sizeof (WidthType),
125  "WidthType must not be wider than size_t");
126  static std::size_t const max = std::numeric_limits<WidthType>::max ();
127  std::size_t result = 0;
128  while (size > 0) {
129  auto const chunk_size = static_cast<WidthType> (std::min (max, size));
130  result += function (buffer, chunk_size);
131  buffer += chunk_size;
132  size -= chunk_size;
133  }
134  return result;
135  }
136 
137  template <typename WidthType, typename Function>
138  std::size_t split (void * const buffer, std::size_t const size,
139  Function const & function) {
140  return split<WidthType> (static_cast<std::uint8_t *> (buffer), size, function);
141  }
142  template <typename WidthType, typename Function>
143  std::size_t split (void const * const buffer, std::size_t const size,
144  Function const & function) {
145  return split<WidthType> (static_cast<std::uint8_t const *> (buffer), size,
146  function);
147  }
148 
149  } // end namespace details
150 
151 
152  class system_error : public std::system_error {
153  public:
154  system_error (std::error_code code, std::string const & user_message, std::string path);
155  system_error (std::error_code code, gsl::czstring user_message, std::string path);
156 
157  system_error (system_error const &) = default;
158  system_error (system_error &&) = default;
159 
160  ~system_error () noexcept override;
161 
162  system_error & operator= (system_error const &) = default;
163  system_error & operator= (system_error &&) = default;
164 
165  std::string const & path () const noexcept { return path_; }
166 
167  private:
168  std::string path_;
169 
170  template <typename MessageStringType>
171  std::string message (MessageStringType const & user_message, std::string const & path);
172  };
173 
174 
176  class file_base {
177  public:
178  file_base (file_base &&) noexcept = default;
179  file_base (file_base const &) = default;
180  virtual ~file_base () noexcept;
181 
182  file_base & operator= (file_base &&) noexcept = default;
183  file_base & operator= (file_base const &) = default;
184 
185  virtual bool is_open () const noexcept = 0;
186  virtual void close () = 0;
187 
191  virtual bool is_writable () const noexcept = 0;
192 
197  virtual std::string path () const = 0;
198 
204  virtual void seek (std::uint64_t position) = 0;
205 
207  virtual std::uint64_t tell () = 0;
208 
209  virtual std::time_t latest_time () const = 0;
210 
213 
218  template <typename SpanType, typename = typename std::enable_if<std::is_standard_layout<
219  typename SpanType::element_type>::value>::type>
220  std::size_t read_span (SpanType const & s) {
221  auto const size = s.size_bytes ();
222  PSTORE_ASSERT (size >= 0);
223  using utype = typename std::make_unsigned<decltype (size)>::type;
224  return this->read_buffer (s.data (), static_cast<utype> (s.size_bytes ()));
225  }
226 
228  template <typename T,
229  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
230  void read (T * const t) {
231  PSTORE_ASSERT (t != nullptr);
232  if (this->read_buffer (t, sizeof (T)) != sizeof (T)) {
233  raise (error_code::did_not_read_number_of_bytes_requested);
234  }
235  }
237 
240  template <typename SpanType, typename = typename std::enable_if<std::is_standard_layout<
241  typename SpanType::element_type>::value>::type>
242  void write_span (SpanType const & s) {
243  auto const bytes = s.size_bytes ();
244  PSTORE_ASSERT (bytes >= 0);
245  auto const ubytes =
246  static_cast<typename std::make_unsigned<decltype (bytes)>::type> (bytes);
247  this->write_buffer (s.data (), ubytes);
248  }
249 
251  template <typename T,
252  typename = typename std::enable_if<std::is_standard_layout<T>::value>::type>
253  void write (T const & t) {
254  this->write_buffer (&t, sizeof (T));
255  }
257 
258 
259  virtual std::uint64_t size () = 0;
260  virtual void truncate (std::uint64_t size) = 0;
261 
275 
279  enum class blocking_mode {
280  non_blocking,
281  blocking,
282  };
285  enum class lock_kind {
286  shared_read,
287  exclusive_write,
288  };
289 
306  virtual bool lock (std::uint64_t offset, std::size_t size, lock_kind lt,
307  blocking_mode bl) = 0;
308 
319  virtual void unlock (std::uint64_t offset, std::size_t size) = 0;
321 
322  protected:
323  file_base () noexcept = default;
324 
330 
340  virtual std::size_t read_buffer (gsl::not_null<void *> buffer, std::size_t nbytes) = 0;
341 
350  virtual void write_buffer (gsl::not_null<void const *> buffer, std::size_t nbytes) = 0;
351 
353  };
354 
355 
356  //* _ _ *
357  //* _ __ __ _ _ __ __ _ ___ | | ___ ___| | __ *
358  //* | '__/ _` | '_ \ / _` |/ _ \ | |/ _ \ / __| |/ / *
359  //* | | | (_| | | | | (_| | __/ | | (_) | (__| < *
360  //* |_| \__,_|_| |_|\__, |\___| |_|\___/ \___|_|\_\ *
361  //* |___/ *
381 
382  class range_lock {
383  public:
390  range_lock () noexcept = default;
395  range_lock (file_base * const file, std::uint64_t offset, std::size_t size,
396  file_base::lock_kind kind) noexcept;
397  range_lock (range_lock const & other) = delete;
398  range_lock (range_lock && other) noexcept;
399 
402  ~range_lock () noexcept;
403 
408  range_lock & operator= (range_lock && other) noexcept;
409  range_lock & operator= (range_lock const &) = delete;
410 
415  bool lock ();
416 
420  bool try_lock ();
421 
424  void unlock ();
425 
428  file_base * file () noexcept { return file_; }
429  file_base const * file () const noexcept { return file_; }
430 
432  std::uint64_t offset () const noexcept { return offset_; }
434  std::size_t size () const noexcept { return size_; }
436  file_base::lock_kind kind () const noexcept { return kind_; }
437  bool is_locked () const noexcept { return locked_; }
439 
440  private:
442  file_base * file_ = nullptr;
444  std::uint64_t offset_ = 0U;
446  std::size_t size_ = 0U;
450  bool locked_ = false;
451 
452  bool lock_impl (file_base::blocking_mode mode);
453 
454 #ifdef PSTORE_HAVE_IS_TRIVIALLY_COPYABLE
455  static_assert (
456  std::is_trivially_copyable<decltype (file_)>::value,
457  "file_ is not trivially copyable: use std::move() in move ctor and assign");
458  static_assert (
459  std::is_trivially_copyable<decltype (offset_)>::value,
460  "offset_ is not trivially copyable: use std::move() in move ctor and assign");
461  static_assert (
462  std::is_trivially_copyable<decltype (size_)>::value,
463  "size_ is not trivially copyable: use std::move() in move ctor and assign");
464  static_assert (
465  std::is_trivially_copyable<decltype (kind_)>::value,
466  "kind_ is not trivially copyable: use std::move() in move ctor and assign");
467  static_assert (
468  std::is_trivially_copyable<decltype (locked_)>::value,
469  "locked_ is not trivially copyable: use std::move() in move ctor and assign");
470 #endif // PSTORE_HAVE_IS_TRIVIALLY_COPYABLE
471  };
472 
473 
474 
475  //* _ *
476  //* (_)_ __ _ __ ___ ___ _ __ ___ ___ _ __ _ _ *
477  //* | | '_ \ | '_ ` _ \ / _ \ '_ ` _ \ / _ \| '__| | | | *
478  //* | | | | | | | | | | | __/ | | | | | (_) | | | |_| | *
479  //* |_|_| |_| |_| |_| |_|\___|_| |_| |_|\___/|_| \__, | *
480  //* |___/ *
483  class in_memory final : public file_base {
484  public:
487 
488  in_memory (std::shared_ptr<void> const & buffer, std::uint64_t const length,
489  std::uint64_t const eof = 0, bool const writable = true) noexcept
490  : buffer_ (std::static_pointer_cast<std::uint8_t> (buffer))
491  , length_ (length)
492  , eof_ (eof)
493  , writable_ (writable) {
494 
495  PSTORE_ASSERT (buffer != nullptr);
496  PSTORE_ASSERT (eof <= length);
497  }
498 
499  void close () override {}
500  bool is_open () const noexcept override { return true; }
501  bool is_writable () const noexcept override { return writable_; }
502  std::string path () const override;
503 
504  void seek (std::uint64_t position) override;
505  std::uint64_t tell () override { return pos_; }
506 
507  std::uint64_t size () override { return eof_; }
508  void truncate (std::uint64_t size) override;
509  std::time_t latest_time () const override;
510 
511 
512  bool lock (std::uint64_t const /*offset*/, std::size_t const /*size*/,
513  lock_kind const /*lt*/, blocking_mode const /*bl*/) override {
514  return true;
515  }
516  void unlock (std::uint64_t const /*offset*/, std::size_t const /*size*/) override {}
517 
519  std::shared_ptr<void> data () { return buffer_; }
520 
527  std::size_t read_buffer (gsl::not_null<void *> buffer, std::size_t nbytes) override;
528 
534  void write_buffer (gsl::not_null<void const *> ptr, std::size_t nbytes) override;
535 
536  private:
538  std::shared_ptr<std::uint8_t> buffer_;
539 
541  std::uint64_t const length_;
542 
545  std::uint64_t eof_;
546 
550  bool writable_;
551 
553  std::uint64_t pos_ = UINT64_C (0);
554 
555  void check_writable () const;
556  };
557 
558 
559  //* __ _ _ _ _ _ *
560  //* / _(_) | ___ | |__ __ _ _ __ __| | | ___ *
561  //* | |_| | |/ _ \ | '_ \ / _` | '_ \ / _` | |/ _ \ *
562  //* | _| | | __/ | | | | (_| | | | | (_| | | __/ *
563  //* |_| |_|_|\___| |_| |_|\__,_|_| |_|\__,_|_|\___| *
564  //* *
566  class file_handle final : public file_base {
567  public:
570 
571  enum class create_mode {
572  create_new,
573  open_existing,
574  open_always
575  };
576 
580  enum class present_mode {
586  allow_not_found,
587 
590  must_exist
591  };
595  enum class writable_mode {
596  read_only,
597  read_write,
598  };
599 
601  struct unique {};
604  struct temporary {};
605 
606 
607  file_handle () = default;
608  explicit file_handle (std::string path) noexcept
609  : path_{std::move (path)} {}
610  file_handle (file_handle const &) = delete;
611  file_handle (file_handle && other) noexcept;
612 
613  ~file_handle () noexcept override;
614 
615  file_handle & operator= (file_handle && rhs) noexcept;
616  file_handle & operator= (file_handle const &) = delete;
617 
618 
619  void open (create_mode create, writable_mode writable,
620  present_mode present = present_mode::allow_not_found);
622  void open (unique, std::string const & directory);
623 
625  void open (temporary const t) {
626  return open (t, file_handle::get_temporary_directory ());
627  }
629  void open (temporary, std::string const & directory);
630 
631 
638  static std::string get_temporary_directory ();
639 
640  bool is_open () const noexcept override { return file_ != invalid_oshandle; }
641  bool is_writable () const noexcept override { return is_writable_; }
642  std::string path () const override { return path_; }
643 
644  void close () override;
645 
646  void seek (std::uint64_t position) override;
647  std::uint64_t tell () override;
648  std::uint64_t size () override;
649  void truncate (std::uint64_t size) override;
653  bool rename (std::string const & new_name);
654 
655  std::time_t latest_time () const override;
656 
657  bool lock (std::uint64_t offset, std::size_t size, lock_kind kind,
658  blocking_mode block) override;
659  void unlock (std::uint64_t offset, std::size_t size) override;
660 
661 #ifdef _WIN32
662  using oshandle = HANDLE;
663  // TODO: making invalid_oshandle constexpr results in it having the value 0 (rather than
664  // -1).
665  static oshandle const invalid_oshandle;
666 #else
667  using oshandle = int;
668  static constexpr oshandle invalid_oshandle = -1;
669 #endif
670 
671  oshandle raw_handle () noexcept { return file_; }
672 
673  private:
674  std::size_t read_buffer (gsl::not_null<void *> buffer, std::size_t nbytes) override;
675  void write_buffer (gsl::not_null<void const *> buffer, std::size_t nbytes) override;
676  void ensure_open ();
677  static error_or<oshandle> close_noex (oshandle const file);
678 
679 #ifdef _WIN32
680 #else
681  static int lock_reg (int fd, int cmd, short type, off_t offset, short whence,
682  off_t len);
683 #endif
684  std::string path_ = "<unknown>";
685  oshandle file_ = invalid_oshandle;
686  bool is_writable_ = false;
687 
688 #ifdef PSTORE_HAVE_IS_TRIVIALLY_COPYABLE
689  static_assert (
690  std::is_trivially_copyable<decltype (file_)>::value,
691  "file_ is not trivially copyable: use std::move() in rvalue ref ctor and assign");
692  static_assert (std::is_trivially_copyable<decltype (is_writable_)>::value,
693  "is_writable_ is not trivially copyable: use std::move() in rvalue ref "
694  "ctor and assign");
695 #endif // PSTORE_HAVE_IS_TRIVIALLY_COPYABLE
696  };
697 
698  // ensure_open
699  // ~~~~~~~~~~~
700  inline void file_handle::ensure_open () {
701  if (!this->is_open ()) {
702  raise (std::errc::bad_file_descriptor);
703  }
704  }
705 
706  std::ostream & operator<< (std::ostream & os, file_handle const & fh);
707 
708 
709  //* _ _ _ _ *
710  //* __| | ___| | ___| |_ ___ _ __ | |__ __ _ ___ ___ *
711  //* / _` |/ _ \ |/ _ \ __/ _ \ '__| | '_ \ / _` / __|/ _ \ *
712  //* | (_| | __/ | __/ || __/ | | |_) | (_| \__ \ __/ *
713  //* \__,_|\___|_|\___|\__\___|_| |_.__/ \__,_|___/\___| *
714  //* *
719  class deleter_base {
720  public:
721  using unlink_proc = std::function<void (std::string const &)>;
722 
723  virtual ~deleter_base () noexcept;
724 
725  // No copying, moving, or assignment
726  deleter_base (deleter_base const &) = delete;
727  deleter_base (deleter_base &&) noexcept = delete;
728  deleter_base & operator= (deleter_base const &) = delete;
729  deleter_base & operator= (deleter_base &&) noexcept = delete;
730 
737  void unlink ();
738 
741  void release () noexcept { released_ = true; }
742 
743  protected:
744  explicit deleter_base (std::string path, unlink_proc unlinker);
745 
746  private:
749  std::string path_;
750 
754  unlink_proc unlinker_;
755 
758  bool released_ = false;
759  };
760 
761 
767  bool exists (std::string const & path);
768 
772  void unlink (std::string const & path, bool allow_noent = false);
773 
774  } // end namespace file
775 } // end namespace pstore
776 
777 #if defined(_WIN32)
778 # include "file_win32.hpp"
779 #else
780 # include "file_posix.hpp"
781 #endif
782 
783 #endif // PSTORE_OS_FILE_HPP
blocking_mode
Indicates whether the lock() method should block until the lock has been obtained.
Definition: file.hpp:279
present_mode
Controls the behavior of the file_handle constructor when passed a path which does not already exist...
Definition: file.hpp:580
Specifies a read (or shared) lock.
Windows-specific implementations of file APIs.
error_or<T> holds either an instance of T or a std:error.
void unlock(std::uint64_t const, std::size_t const) override
Unlocks the file bytes specified by &#39;offset&#39; and &#39;size&#39;.
Definition: file.hpp:516
void write(T const &t)
Writes &#39;t&#39; as a series of raw bytes to the file.
Definition: file.hpp:253
std::uint64_t offset() const noexcept
Definition: file.hpp:432
writable_mode
Controls whether the file_handle constructor produces a read-only or read/write object.
Definition: file.hpp:595
void open(temporary const t)
Creates a temporary file in the system temporary directory.
Definition: file.hpp:625
static std::string get_temporary_directory()
Returns a UTF-8 encoded string representing the temporary directory.
Definition: file_posix.cpp:474
Definition: chunked_sequence.hpp:607
memory_mapper provides an operating system independent interface for memory mapping of files...
Definition: memory_mapper.hpp:157
Definition: error_or.hpp:39
unique is an empty class type used to disambiguate the overloads of creating a file.
Definition: file.hpp:601
Definition: gsl.hpp:589
std::size_t size() const noexcept
Definition: file.hpp:434
file_base::lock_kind kind() const noexcept
Definition: file.hpp:436
Implements a portable file access API.
Definition: file.hpp:566
temporary is an empty class type used to disambiguate the overloads of creating a file...
Definition: file.hpp:604
Definition: print.cpp:18
void release() noexcept
Releases the file path given to the constructor so that it will not be deleted when the instance is d...
Definition: file.hpp:741
A synchronization object that can be used to protect data in a file from being simultaneously accesse...
Definition: file.hpp:382
create_mode
Definition: file.hpp:571
bool exists(std::string const &path)
Returns true if the file system contains an object at the location given by path. ...
Definition: file_posix.cpp:505
void write_span(SpanType const &s)
Writes instances of a standard-layout type to the file.
Definition: file.hpp:242
Implements an in-memory file which provides a file-like API to a chunk of pre-allocated memory...
Definition: file.hpp:483
lock_kind
Represents the type of file range lock to be obtained.
Definition: file.hpp:285
Definition: nonpod2.cpp:40
bool is_writable() const noexcept override
Return true if the object was created as writable.
Definition: file.hpp:641
bool is_writable() const noexcept override
Return true if the object was created as writable.
Definition: file.hpp:501
void unlink(std::string const &path, bool allow_noent=false)
Deletes the file system object at the location given by path.
Definition: file_posix.cpp:507
bool lock(std::uint64_t const, std::size_t const, lock_kind const, blocking_mode const) override
Obtains a shared-read or exclusive-write lock on the file range specified by the &#39;offset&#39; and size&#39; p...
Definition: file.hpp:512
std::size_t read_span(SpanType const &s)
Reads instances of a standard-layout type from the file.
Definition: file.hpp:220
Provides an pstore-specific error codes and a suitable error category for them.
std::string path() const override
Returns the name of the file originally associated with this file object.
Definition: file.hpp:642
std::shared_ptr< void > data()
Returns the underlying memory managed by the file object.
Definition: file.hpp:519
std::size_t split(PointeeType *buffer, std::size_t size, Function const &function)
Unfortunately, the Win32 ReadFile() and WriteFile() functions accept size parameters whose type is DW...
Definition: file.hpp:119
std::string name_from_template(std::string const &tmpl, RandomGenerator rng)
The name_from_template() function takes the given file name template and returns a string in which a ...
Definition: file.hpp:72
Definition: memory_mapper.hpp:177
void read(T *const t)
Reads a series of raw bytes from the file as an instance of type T.
Definition: file.hpp:230
std::uint64_t tell() override
Obtains the current value of the position indicator for the file.
Definition: file.hpp:505
A class which, on destruction, will delete a file whose name is passed to the constructor.
Definition: file.hpp:719
Definition: file.hpp:152
An abstract file class. Provides the interface for file access.
Definition: file.hpp:176
Posix-specific implementations of file APIs.