Commit Graph

205 Commits

Author SHA1 Message Date
Giuseppe Scrivano 28cba3014b
chunked: store compressed digest if validated
if the compressed digest was validated, as it happens when
'pull_options = {convert_images = "true"}' is set, then store it as
well so that reusing the blob by its compressed digest works.

Previously, when an image converted to zstd:chunked was pulled a
second time, it would not be recognized by its compressed digest,
resulting in the need to re-pull the image again.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-07-10 09:41:41 +02:00
Giuseppe Scrivano 0ab61dfb6c
store: lock both graphroot and the imagestore root
when an image store is used, lock it as well as the graphroot.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-02-29 16:35:58 +01:00
Giuseppe Scrivano f31412a314
layers: add infra to lock multiple files
this is a preparation for the next commit, which will use this feature.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-02-29 16:34:29 +01:00
Miloslav Trmač 41a2f9d942 Save our layer before calling SetBigData and other large I/O
SetBigData itself calls saveFor; so doing that before raises
fewer questions about stale data / stepping over each other.

The change in timing is externally-observable, but should hopefully
not matter much in practice, because this code is typically called
from layerStore.create as a part of an atomic create+populate operation
proptected by incompleteFlag.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2024-02-26 21:04:14 +01:00
Giuseppe Scrivano 21ed482eca
store: new API ApplyStagedLayer
Add a race-condition-free alternative to using CreateLayer and
ApplyDiffFromStagingDirectory, ensuring the store is locked for the
entire duration while the layer is being created and populated.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-02-15 21:57:00 +01:00
Giuseppe Scrivano c6de01cf31
driver: simplify ApplyDiffFromStagingDirectory
enforce that the stagingDirectory must have the same value as the
diffOutput.Target variable.  It allows to simplify the internal API.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2024-02-15 21:56:59 +01:00
Miloslav Trmač 2ca0e2772a Add LayerOptions.OriginalSize
This allows us to correctly set (CompresedDigest, CompressedSize)
when copying data from another layer; in that case we don't have the
compressed data, so computing the size from compressedCounter
sets an incorrect value.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2024-02-13 21:56:05 +01:00
Miloslav Trmač 52ab5bfec8 Optimize digest computation for uncompressed layers
When the caller provides neither OriginalDigest nor
UncompressedDigest, only digest the layer once.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2024-02-13 21:51:10 +01:00
Giuseppe Scrivano 17bc7c5bfb
overlay: inhibit filegetter with composefs
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2023-12-11 15:06:41 +01:00
Giuseppe Scrivano 15ac716f16
layers: add option to initialize layer Flags
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2023-12-04 16:58:13 +01:00
Giuseppe Scrivano f424bfcf9f
store: new struct to pass ApplyDiffWithDiffer opts
Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2023-12-04 16:58:13 +01:00
Giuseppe Scrivano 106e2d1730
layers: add new TOCDigest attribute
introduce the TOCDigest field for a layer. TOCDigest is designed to
store the digest of the Table of Contents (TOC) of the blob.

It is useful when the UncompressedDigest cannot be validated during a
partial image pull, but the TOC itself is validated.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2023-12-04 16:58:13 +01:00
Miloslav Trmač 58bb75773b Don't call UpdateLayerIDMap when creating a layer with no parent
AFAICS this call is intended to "remap" the parent layer's contents to the
desired IDMappings; but when there is no parent layer, there is
nothing to remap.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-10-25 16:45:49 +02:00
Miloslav Trmač 339628c8f5 Shorten the scope of some variables
Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-10-25 16:45:37 +02:00
Giuseppe Scrivano 7bbf6ed448
chunked: generate tar-split as part of zstd:chunked
change the file format to store the tar-split as part of the
zstd:chunked image.  This will allow clients to rebuild the entire
tarball without having to download it fully.

also store the uncompressed digest for the tarball, so that it can be
stored into the storage database.

Needs: https://github.com/containers/image/pull/1976

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2023-06-17 00:31:39 +02:00
Nalin Dahyabhai 79b8f9401e compareCheckDirectory: learn about ID maps
Handle old-fashioned ID mappings when looking at layers.  Nowadays,
we'll use an idmapped mount if we can, but we shouldn't blow up if we
had to chown a layer because we couldn't use an idmapped mount.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2023-06-13 14:14:56 -04:00
Miloslav Trmač 02cb8ed5fe Ensure compressor.Close() is called on error paths
Reportedly this is showing up as leaked goroutines in CRI-O:
https://issues.redhat.com/browse/OCPBUGS-10290 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-06-13 02:17:30 +02:00
Miloslav Trmač f2a1367f54 Fix a race in use of tarlog.tarLogger
tarLogger calls the provided callback in a separate
goroutine, and that can happen after tarLogger.Write
returns; tarLogger.Close is requried to ensure the callbacks
have all been correctly called, and the created uidLog and gidLog
values can be consumed.

So, move most of the IO pipeline that is formed around the
layer stream into a nested function that terminates earlier, notably
so that the "defer idLogger.Close()" is called at the appropriate time.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-06-13 02:16:37 +02:00
Miloslav Trmač a6b3f79a56 Move some variable declarations upfront
We will want to move the next part of the code
into a closure; move variables that will be
accessed outside of that section.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-06-13 02:06:21 +02:00
Miloslav Trmač 867f763e81 Don't ignore pgzip.NewWriterLevel failures
AFAICS that can't fail with current pgzip; and
pgzip.NewWriter also calls NewWriteLevel, but it just
swallows the error.

Any failure would therefore be very unexpected;
report it instead of suppressing it.0

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-06-13 01:35:42 +02:00
Kir Kolyshkin a4d8f720a2 Format sources with gofumpt
gofumpt is a superset of gofmt, enabling some more code formatting
rules.

This commit is brought to you by

	gofumpt -w .

Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
2023-05-26 16:17:31 -07:00
Miloslav Trmač 96fd3ff643 Don't silently ignore errors decoding mountpoints.json
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-05-19 18:51:36 +02:00
Giuseppe Scrivano ed76ab945c
store: ensure lockfile is updated before writes
The lockfile's write record is now updated prior to the actual write
operation.  This ensures that, in the event of an unexpected
termination, other processes are correctly notified of an initiated
write operation.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
2023-05-18 23:44:23 +02:00
Miloslav Trmač 90fa73079a Don't use bytes.NewBuffer to read data
The documentation says
> The new Buffer takes ownership of buf, and the
> caller should not use buf after this call.

so use the more directly applicable, and simpler, bytes.Reader
instead, to avoid this potentially risky use.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-04-14 22:34:53 +02:00
Nalin Dahyabhai d10c03bc3d Extend driver.ListLayers()
Implement ListLayers() for the aufs, btrfs, and devicemapper drivers,
along with a unit test for them.
Stop filtering out directories with names that aren't 64-hex chars in
vfs and overlay ListLayers() implementations, which is more a convention
than a hard rule.
Have layerStore.Wipe() try to remove remaining listed layers after it
removes the layers that the layerStore knew of.
Close() a dangling ReadCloser in NaiveCreateFromTemplate.
Switch from using plain defer to using t.Cleanup() to handle deleting
layers that tests create, have the addManyLayers() test function do so
as well.
Remove vfs.CopyDir, which near as I can tell isn't referenced anywhere.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2023-04-12 16:05:09 -04:00
Nalin Dahyabhai b0c1c886c3 "pull up" images when creating them, too
We previously started "pulling up" images when we changed their names,
and started denying the presence of images in read-only stores which
shared their ID with an image in the read-write store, so that it would
be possible to "remove" names from an image in read-only storage.  We
forgot about the Flags field, so start pulling that up, too.

Do all of the above when we're asked to create an image, since denying
the presence of images with the same ID in read-only stores would
prevent us from finding the image by any of the names that it "had" just
a moment before we created the new record.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2023-04-06 18:21:11 -04:00
Nalin Dahyabhai 0f2bccfa56 Complete "pulling up" of images in updateNames()
When updateNames() copies an image's record from a read-only store into
the read-write store, copy the accompanying data as well.

Add fields for setting data items at creation-time to LayerOptions,
ImageOptions, and ContainerOptions to make this easier for us and our
consumers.

Replace the store-specific Create() (and the one CreateWithFlags() and
Put()) with private create() and put() methods, since they're not
intended for consumption outside of this package, and add Flags to the
options structures we pass into those methods.  In create() methods,
make copies of those passed-in options structures before modifying any
of their contents.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2023-03-31 10:36:30 -04:00
Daniel J Walsh 0ee26255cd
Run codespell on codebase
Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
2023-02-09 12:27:59 -05:00
Nalin Dahyabhai 6d91bc12f3 cmd: add a CLI wrapper for GarbageCollect
Add "gc" as an action for the CLI wrapper, for running the
GarbageCollect() method.

Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
2023-01-26 16:09:00 -05:00
Miloslav Trmač ffeaed8fa4 Optimize the "no changes" case of layerStore.startReading further
In that case, we can just get read locks, confirm that nothing has changed,
and continue; no need for any serialization on exclusively holding
loadMut / inProcessLock.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-04 00:29:39 +01:00
Miloslav Trmač 717d171f22 Rework in-process concurrency control of layerStore
Instead of basing this on exclusivity loading via loadMut (which was incorrect,
because contrary to the original design, the r.layerspathModified
check in r.Modified() could trigger during the lifetime of a read lock)
use a very traditional read-write lock to protect the fields of imageStore.

Also explicitly document how concurrent access to fields of imageStore
is managed.

Note that for the btrfs and zfs graph drivers, Diff() can trigger
Mount() and unmount() in a way that violates the locking design.
That's not fixed in this PR.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-04 00:29:27 +01:00
Miloslav Trmač d173b026cb Annotate layerStore methods with the required lock access
Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-04 00:29:27 +01:00
Miloslav Trmač 4950eef972 Don't fix, just document, a locking bug
This should be fixed, it just seems too hard to do without
breaking API (and performance).

So, just be clear about that to warn future readers.

It's tracked in https://github.com/containers/storage/issues/1379 .

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-04 00:29:26 +01:00
Miloslav Trmač 16b28bae49 Don't reload the mount data in ParentOwners and Mounted
We can't safely do that because the read-only callers don't allow us
to write to layerStore state.

Luckily, with the recent changes to Mounted, we don't really need to
reload in those places.

Also, fairly extensively document the locking design or implications
for users.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-04 00:29:26 +01:00
Miloslav Trmač 845c2f4a30 Eliminate racy uses of Layer.MountCount
Instead of reading that value, releasing the mount lock,
and then unmounting, provide a "conditional" unmount mode.
And use that in the existing "loop unmounting" code.

That's at least safer against concurrent processes unmounting
the same layer. But the callers that try to "really unmount"
the layer in a loop are still possibly racing against other processes
trying to mount the layer in the meantime.

I'm not quite sure that we need the "conditional" parameter as an
explicit choice; it seems fairly likely that Umount() should just fail
with ErrLayerNotMounted for all !force callers. I chose to use the flag
to be conservative WRT possible unknown constraints.

Similarly, it's not very clear to me that the unmount loops need to exist;
maybe they should just be unmount(force=true, conditional=true).

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2023-01-03 23:47:16 +01:00
Alexander Larsson e546955f90 layer store: Don't reload the layer store after we save it
The lockfile we use propertly handles the case that we Touch() it. In
other words, a later Modified() call will return false.

However, we're also looking at the mtime, which was failing. This
uses the new AtomicWriteFileWithOpts() feature to also record the
mtime of the file we write on updates.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
2022-12-09 08:57:13 +01:00
Miloslav Trmač ac110ce1be Fix locking in store.canUseShifting
This was using the graphDriver field without locks, and the graph driver itself,
while the implementation assumed exclusivity.

Luckily all callers are actually holding the layer store lock for writing, so
use that for exclusion. (layerStore already seems to extensively assume
that locking the layer store for writing guarantees exclusive access to the graph driver,
and because we always recreate a layer store after recreating the graph driver,
that is true in practice.)

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-12-05 18:50:13 +01:00
Miloslav Trmač 55d11a0389 Make change tracking error-resilient
Only update recorded LastWrite values _after_ we succesfully reload
container/image/layer stores; so, if the reload fails, the functions
will keep failing instead of using obsolete (and possibly partially loaded
and completely invalid) data.

Also, further improve mount change tracking, so that if layerStore.load()
loads it, we don't reload it afterwards.

This does not change the store.graphLock users; they will need to be cleaned up
more comprehensively, and separately, in the future.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-29 18:12:43 +01:00
Miloslav Trmač b8bcdf7681 Improve mounts reloads
If the mounts file is changed, don't reload all of the layers
as well. This is more efficient, and it will allow us to better
track/update r.lastWrite and r.mountsLastWrite in the future.

Exiting code calling reloadMountsIfChanged() indicates that this
must already be safe.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-29 18:12:43 +01:00
Miloslav Trmač b2d3379ed4 Deprecate *LockFile.Modified and *LockFile.Touch
They are a shared state across all users of the *LockFile in the process,
and therefore incorrect to use for any single consumer for change tracking.

Direct users to user the new GetLastWrite, ModifiedSince, and RecordWrite,
instead, and convert all c/storage users.

In addition to just being correct, the new API is also more efficient:
- We now initialize stores with GetLastWrite before loading state;
  so, we don't trigger an immediate reload on the _second_ use of a store,
  due to the problematic semantics of .Modified().
- Unlike Modified() and Touch(), the new APi can be safely used without
  locking *LockFile.stateMutex, for a tiny speedup.

The conversion is, for now, trivial, just immediately updating the lastWrite
value after the ModifiedSince call.  That will get better.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-29 18:12:43 +01:00
Miloslav Trmač b74fdf4d23 Convert all c/storage users of Locker to *lockfile.LockFile
This will allow us to benefit from new features.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-29 18:12:43 +01:00
Miloslav Trmač f1c6fb0ce7 Factor out layerStore.reloadMountsIfChanged
... so that we don't need to update 4 separate copies.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-29 18:12:43 +01:00
Miloslav Trmač d24b8c5a13 Fix layerStore.saveLayers
- Don't exit after saving only one of the locations
- Only touch the lock once if saving both of the locations

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-29 18:12:43 +01:00
Alexander Larsson ddf18d41da Add Store.GarbageCollect() method
This looks in the container store for existing data dirs with ids not in
the container files and removes them. It also adds an (optional) driver
method to list available layers, then uses this and compares it to the
layers json file and removes layers that are not references.

Losing track of containers and layers can potentially happen in the
case of some kind of unclean shutdown, but mainly it happens at reboot
when using transient storage mode. Such users are recommended to run
a garbage collect at boot.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
2022-11-14 16:36:30 +01:00
Alexander Larsson 58bf1c03e4 layer store: Handle volatile and transient layer
We store information about the layers in two files, layers.json and
volatile-layers.json. This allows us to treat the two stores
differently, for example saving the volatile json with the NoSync
option, which is faster (but less robust).

In normal mode we store only layers for the containers that are marked
volatile (`--rm`), as these are not expected to persist anyway. This way
informantion about such containers are faster to save, and in case
of an unclean shutdown we only risk loss of information about other
such volatile containers.

In transient mode all container layers (but not image layers) are
stored in the volatile json, and additionally it is stored in tmpfs.
This improving performance as well as automatically making sure no
container layers are persisted across boots.

Signed-off-by: Alexander Larsson <alexl@redhat.com>
2022-11-14 16:36:30 +01:00
Miloslav Trmač 7f6a670c44 Clean up inconsistent layer state in readers
... instead of failing for duplicate names, and instead of
ignoring the "incomplete" state of layers.

Try up to 3 times if other writers are creating inconsistent
state in the meantime.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-10 16:13:53 +01:00
Miloslav Trmač 48c7d2048f Be a bit more accurate about incomplete layer deletion
- In read-write stores, fail even readers if there are incomplete
  layers.  Right now that would increase observed failures (OTOH
  with better consistency), but we'll fix that again soon
- Decide whether to save read-write stores strictly based on
  the need to clean up, instead of cleaning up opportunistically
  (which is less predictable).
- Correctly return the right error, depending on whether there
  are duplicate layers

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-10 16:13:53 +01:00
Miloslav Trmač 859d1791b8 In load methods, track the inconsistency we want to fix
... as an error value instead of just a boolean.  That
will allow extending the logic to more kinds of inconsistencies,
and consolidates the specifics of the inconsistency knowledge
into a single place.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-10 16:13:53 +01:00
Miloslav Trmač 765414fbdb Use the simplest possible error handling flow in reloadIfChanged
... instead of combining control flow branches; we will change
behavior on some of them.

Should not change behavior.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-11-10 16:13:53 +01:00
Miloslav Trmač f6776bf5dc Remove always-true err == nil checks
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
2022-10-25 20:54:12 +02:00