GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/swissknife_overlay.h
Date: 2026-05-03 02:36:16
Exec Total Coverage
Lines: 0 6 0.0%
Branches: 0 0 -%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * Swissknife command for performing catalog-level overlays of multiple
5 * subdirectories from a CVMFS repository, similar to OverlayFS or how
6 * container engines merge container image layers.
7 *
8 * The overlay tool merges the catalog entries of multiple layer subdirectories,
9 * typically corresponding to container layers, and publishes the result as
10 * a new subdirectory in the CVMFS repository, typically corresponding to the flat
11 * root file system of the container image.
12 *
13 * The publish workflow is taken from swissknife ingest.
14 */
15
16 #ifndef CVMFS_SWISSKNIFE_OVERLAY_H_
17 #define CVMFS_SWISSKNIFE_OVERLAY_H_
18
19 #include <map>
20 #include <string>
21 #include <vector>
22
23 #include "catalog.h"
24 #include "catalog_mgr_rw.h"
25 #include "directory_entry.h"
26 #include "swissknife.h"
27 #include "upload.h"
28 #include "xattr.h"
29
30 namespace swissknife {
31
32 /**
33 * Represents a single entry in the merged overlay catalog, tracking
34 * which layer it came from and its metadata.
35 */
36 struct OverlayEntry {
37 catalog::DirectoryEntry entry;
38 XattrList xattrs;
39 std::string path; // relative path in the merged view
40 std::string parent; // parent directory relative path in the merged view
41 bool is_whiteout; // true if this is a whiteout marker
42 bool is_opaque_dir; // true if directory has opaque marker
43
44 OverlayEntry()
45 : is_whiteout(false), is_opaque_dir(false) {}
46 };
47
48 class CommandOverlay : public Command {
49 public:
50 virtual ~CommandOverlay() {}
51
52 virtual std::string GetName() const { return "overlay"; }
53 virtual std::string GetDescription() const {
54 return "Merge multiple CVMFS subdirectory catalogs using overlay "
55 "semantics and publish the result as a repository subdirectory";
56 }
57 virtual ParameterList GetParams() const;
58 virtual int Main(const ArgumentList &args);
59
60 private:
61 /**
62 * Recursively read all entries from a catalog rooted at the given path.
63 * The entries are stored with paths relative to the layer root.
64 * When a nested catalog mountpoint is encountered and repo_base/temp_dir
65 * are non-empty, the nested catalog is loaded and recursed into.
66 */
67 bool ReadCatalogEntries(
68 catalog::Catalog *catalog,
69 const std::string &catalog_root_path,
70 const std::string &relative_prefix,
71 const std::string &repo_base,
72 const std::string &temp_dir,
73 std::map<std::string, OverlayEntry> *entries);
74
75 /**
76 * Check if a filename represents a whiteout file (.wh.<name>).
77 */
78 static bool IsWhiteoutFile(const std::string &name);
79
80 /**
81 * Get the original filename from a whiteout filename.
82 * E.g., ".wh.foo" -> "foo"
83 */
84 static std::string GetWhiteoutTarget(const std::string &name);
85
86 /**
87 * Check if a filename represents an opaque directory marker (.wh..wh..opq).
88 */
89 static bool IsOpaqueMarker(const std::string &name);
90
91 /**
92 * Merge entries from a higher layer into the accumulated overlay map.
93 * Implements overlay semantics: whiteout handling, opaque dirs, overrides.
94 */
95 void MergeLayer(
96 const std::map<std::string, OverlayEntry> &layer_entries,
97 std::map<std::string, OverlayEntry> *merged) const;
98
99 /**
100 * Publish the merged overlay entries into the repository under dest_path
101 * using the WritableCatalogManager. This adds directory and file entries
102 * to the live catalog, then commits the changes to produce a new manifest.
103 */
104 bool PublishMergedEntries(
105 catalog::WritableCatalogManager *catalog_mgr,
106 const std::map<std::string, OverlayEntry> &merged,
107 const std::string &dest_path) const;
108
109 /**
110 * Load a catalog from the repository for a given subdirectory path.
111 * Handles both local and remote repositories.
112 */
113 catalog::Catalog *LoadCatalogForPath(
114 const std::string &repo_base,
115 const std::string &subdirectory,
116 const std::string &temp_dir,
117 const shash::Any &root_hash);
118
119 /**
120 * Find the catalog that contains the given layer path by walking the
121 * nested catalog hierarchy. The layer path may be deep inside a nested
122 * catalog (e.g. /.layers/ab/abcdef.../layerfs where /.layers is a nested
123 * catalog mountpoint). Returns the catalog that can look up layer_path,
124 * or NULL on failure. The caller takes ownership of any additionally
125 * loaded catalogs returned via loaded_catalogs.
126 */
127 catalog::Catalog *FindCatalogForLayer(
128 const std::string &repo_base,
129 const std::string &temp_dir,
130 catalog::Catalog *root_catalog,
131 const std::string &layer_path,
132 std::vector<catalog::Catalog *> *loaded_catalogs);
133
134 /**
135 * Parse an OCI image config JSON file and inject Singularity
136 * compatibility dotfiles (.singularity.d/) into the merged overlay
137 * entries. The generated files include the base environment,
138 * action scripts, runscript (from Entrypoint/Cmd) and environment
139 * variables (from Env).
140 *
141 * File content is uploaded through the spooler so that content hashes
142 * are available for the catalog entries.
143 *
144 * Returns false on error.
145 */
146 bool InjectSingularityDotfiles(
147 const std::string &oci_config_path,
148 upload::Spooler *spooler,
149 std::map<std::string, OverlayEntry> *merged);
150
151 /**
152 * Helper: create an OverlayEntry for a directory.
153 */
154 static OverlayEntry MakeDirEntry(const std::string &path,
155 const std::string &parent);
156
157 /**
158 * Helper: create an OverlayEntry for a regular file, uploading
159 * its content through the spooler and blocking until the content
160 * hash is available.
161 */
162 static OverlayEntry MakeFileEntry(const std::string &path,
163 const std::string &parent,
164 const std::string &content,
165 upload::Spooler *spooler);
166
167 /**
168 * Helper: create an OverlayEntry for a symlink.
169 */
170 static OverlayEntry MakeSymlinkEntry(const std::string &path,
171 const std::string &parent,
172 const std::string &target);
173
174 /**
175 * Shell-escape a string (escape backslash, double-quote, backtick, $).
176 */
177 static std::string ShellEscape(const std::string &s);
178
179 /**
180 * Quote a list of shell args.
181 */
182 static std::string ArgsQuoted(const std::vector<std::string> &args);
183
184 /**
185 * Generate the content of the runscript from OCI Entrypoint and Cmd.
186 */
187 static std::string GenerateRunscript(
188 const std::vector<std::string> &entrypoint,
189 const std::vector<std::string> &cmd);
190
191 /**
192 * Generate the content of env/10-docker2singularity.sh from OCI Env.
193 */
194 static std::string GenerateEnvScript(
195 const std::vector<std::string> &env);
196 };
197
198 } // namespace swissknife
199
200 #endif // CVMFS_SWISSKNIFE_OVERLAY_H_
201
202