(Maint) Clean up code and tests

The way the tests are currently written makes it very hard to get an
overview of all of the behaviors that the code has. This is just the
first step toward getting the tests to be clearer.
(#22918) Lock the filebucket for updates

There were 2 races in the filebucket saving logic:

  1. 2 writers could try to create the `contents` file at the same time.
     This resulted in the writer that loses the race raising an error
     because it did not have write permission to the `contents` file that
     the first writer created.
  2. 2 writers could try updating the `paths` file at the same time.
     This would result in both entering the path in the file thereby
     causing duplicate information in the file. It might also have been
     possible for the two writes to be interleaved in a manner that garbled
     the paths (for instance if the write buffers end up flushing twice in
     an interleaved fashion).

The new method (exclusively_update_file) obtains a LOCK_EX on the
`paths` file before any changes are attempted. This makes the `paths`
file the mutex, not only for updating the paths, but also setting the
contents. This requires that the filesystem supports flock.

Note: the Puppet::Settings.readwritelock method does not work as
intended. It also attempts to get an exclusive lock on a file so that
processes can update it safely, but after testing it out, it actually
does not achieve this. In writing the new method we discovered that the
exclusive lock ability is lost if the update is done using a tempfile
and atomic replace of the file that is locked. When that happens there
is an opportunity for two processes to attain locks and modify the file.
The solution to this was to *only* allow updates-in-place.
(Maint) Introduce File abstraction

Over time we have accumulated various file manipulation operations that
either deal with platform differences of the standard ruby calls or
introduce more useful abstractions for us on top of the ruby calls.
That code has not had a good place to live. Sometimes it appeared on
certain classes, never to be found for use in other areas of the code.
Other times it has been added to the ever growing Puppet::Util mess.

This introduces Puppet::FileSystem::File, which should be used as our
abstraction over ruby's File. We should strive to have it keep a minimal
interface that is geared towards the operations that puppet performs on
files. Inspiration for how to grow it should come, primarily, from uses
found in the puppet codebase as well as from other FS abstraction
systems (such as EFS from eclipse).

This iteration of Puppet::FileSystem::File grew from the file operations
that were done in the filebucket code.
(Maint) Allow File to work on ruby 1.8 and 1.9

Ruby 1.8 does not have the same ::File constants as ruby 1.9
(specifically it did not have ::File::BINARY). Instead of using those
constants, the File abstraction will instead use the character-based
file modes (r, r+, w, w+, wb, etc.).

Pathname on ruby 1.8 also does not support binread. To deal with this,
the File now has 2 subclasses File18 and File19 to handle the
differences on the different systems. The File::new method is now a
factory for the appropriate subclass.
(Maint) Improve readability

if ... else ... end was a bit verbose. Ternary conditional is shorter
and easier to read.
(Maint) Compare filebucket contents with streams

The old code required that we completely read into memory both the current
filebucket contents and the new contents. This doubled the amount of ram
needed when checking if the new contents were the same. Instead of
reading in the entire contents, we can instead work from streams and use
the FileUtils.compare_streams method.
(Maint) Convert uses of IO.binread to P::FS::File

Now that we have Puppet::FileSystem::File we can start using it in more
places and start getting rid of some of the monkey patches that puppet
has been doing.
(Maint) Deprecate (Puppet::Util|IO).binread

IO.binread is a monkey patch, which makes it inherently bad.
Puppet::Util.binread is a trivial implementation that can be handled by
callers instead of us holding onto this in a library.
Merge branch 'issue/master/22918-race-in-filebuckets'

* issue/master/22918-race-in-filebuckets:
  (Maint) Deprecate (Puppet::Util|IO).binread
  (Maint) Convert uses of IO.binread to P::FS::File
  (Maint) Compare filebucket contents with streams
  (Maint) Improve readability
  (Maint) Allow File to work on ruby 1.8 and 1.9
  (Maint) Introduce File abstraction
  (#22918) Lock the filebucket for updates
  (Maint) Clean up code and tests
(#22918) Allow paths file to be read and appended to

Previously, on Windows, the `path_match` method would fail to read the
`paths` file, because it would reopen the file, but an exclusive lock
was already on the file.

This commit changes the `path_match` method so that it takes the
exclusively locked file handle, which must be opened for both read and
append (a+). Note that reads, e.g. `each_line`, occur from the
beginning of the file, while writes occur at the end, so that we don't
have to seek within the file.
(Maint) Exclude `fork` based tests on Windows

We have to skip these tests on Windows because fork is not supported.
Merge branch 'issue/master/22918-race-in-filebuckets'

* issue/master/22918-race-in-filebuckets:
  (Maint) Exclude `fork` based tests on Windows
  (#22918) Allow paths file to be read and appended to

Closes GH-2006