puppetlabs/puppet — pull request #2006 by zaphod42 (2013-10-22 20:50:26+00:00)
Title: (#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 (`Puppet::FileSystem::File.exclusive_open`) 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.

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.