GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/swissknife_info.cc
Date: 2024-04-28 02:33:07
Exec Total Coverage
Lines: 0 133 0.0%
Branches: 0 140 0.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 *
4 * This command reads the content of a .cvmfspublished file and exposes it
5 * to the user.
6 */
7
8 #define __STDC_FORMAT_MACROS
9
10 #include "swissknife_info.h"
11 #include "cvmfs_config.h"
12
13 #include <string>
14
15 #include "crypto/hash.h"
16 #include "manifest.h"
17 #include "network/download.h"
18 #include "util/logging.h"
19 #include "util/posix.h"
20 #include "util/string.h"
21
22 using namespace std; // NOLINT
23
24 namespace swissknife {
25
26 /**
27 * Checks if the given path looks like a remote path
28 */
29 static bool IsRemote(const string &repository) {
30 return HasPrefix(repository, "http://", false) ||
31 HasPrefix(repository, "https://", false);
32 }
33
34 /**
35 * Checks for existence of a file either locally or via HTTP head
36 */
37 bool CommandInfo::Exists(const string &repository, const string &file) const {
38 if (IsRemote(repository)) {
39 const string url = repository + "/" + file;
40 download::JobInfo head(&url, false);
41 return download_manager()->Fetch(&head) == download::kFailOk;
42 } else {
43 return FileExists(file);
44 }
45 }
46
47 ParameterList CommandInfo::GetParams() const {
48 swissknife::ParameterList r;
49 r.push_back(Parameter::Mandatory('r', "repository directory / url"));
50 r.push_back(Parameter::Optional('u', "repository mount point"));
51 r.push_back(Parameter::Optional('l', "log level (0-4, default: 2)"));
52 r.push_back(Parameter::Optional('@', "proxy url"));
53 r.push_back(Parameter::Switch('c', "show root catalog hash"));
54 r.push_back(Parameter::Switch('C', "show mounted root catalog hash"));
55 r.push_back(Parameter::Switch('n', "show fully qualified repository name"));
56 r.push_back(Parameter::Switch('t', "show time stamp"));
57 r.push_back(Parameter::Switch('m',
58 "check if repository is marked as "
59 "replication master copy"));
60 r.push_back(Parameter::Switch('v', "repository revision number"));
61 r.push_back(Parameter::Switch('g',
62 "check if repository is garbage "
63 "collectable"));
64 r.push_back(Parameter::Switch('o',
65 "check if the repository maintains a "
66 "reference log file"));
67 r.push_back(Parameter::Switch('h', "print results in human readable form"));
68 r.push_back(Parameter::Switch('L', "follow HTTP redirects"));
69 r.push_back(Parameter::Switch('X',
70 "show whether external data is supported "
71 "in the root catalog."));
72 r.push_back(Parameter::Switch('M', "print repository meta info."));
73 r.push_back(Parameter::Switch('R', "print raw manifest."));
74 r.push_back(Parameter::Switch('e', "check if the repository is empty"));
75 return r;
76 }
77
78 int swissknife::CommandInfo::Main(const swissknife::ArgumentList &args) {
79 if (args.find('l') != args.end()) {
80 unsigned log_level =
81 kLogLevel0 << String2Uint64(*args.find('l')->second);
82 if (log_level > kLogNone) {
83 LogCvmfs(kLogCvmfs, kLogStderr, "invalid log level");
84 return 1;
85 }
86 SetLogVerbosity(static_cast<LogLevels>(log_level));
87 }
88 const string mount_point =
89 (args.find('u') != args.end()) ? *args.find('u')->second : "";
90 const string repository = MakeCanonicalPath(*args.find('r')->second);
91
92 // sanity check
93 if (args.count('C') > 0 && mount_point.empty()) {
94 LogCvmfs(kLogCvmfs, kLogStderr, "need a CernVM-FS mountpoint (-u) for -C");
95 return 1;
96 }
97
98 if (IsRemote(repository)) {
99 const bool follow_redirects = args.count('L') > 0;
100 const string proxy =
101 (args.find('@') != args.end()) ? *args.find('@')->second : "";
102 if (!this->InitDownloadManager(follow_redirects, proxy)) {
103 return 1;
104 }
105 }
106
107 // Check if we should be human readable
108 const bool human_readable = (args.count('h') > 0);
109
110 if (args.count('e') > 0) {
111 string manifest_path = IsRemote(repository)
112 ? ".cvmfspublished"
113 : repository + "/.cvmfspublished";
114 bool is_empty = !Exists(repository, manifest_path);
115 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
116 (human_readable) ? "Empty Repository: " : "",
117 StringifyBool(is_empty).c_str());
118 if (is_empty) return 0;
119 }
120
121 // Load manifest file
122 // Repository can be HTTP address or on local file system
123 // TODO(jblomer): do this using Manifest::Fetch
124 // currently this is not possible, since Manifest::Fetch asks for the
125 // repository name... Which we want to figure out with the tool at hand.
126 // Possible Fix: Allow for a Manifest::Fetch with an empty name.
127 UniquePtr<manifest::Manifest> manifest;
128 if (IsRemote(repository)) {
129 const string url = repository + "/.cvmfspublished";
130 cvmfs::MemSink manifest_memsink;
131 download::JobInfo download_manifest(&url, false, false, NULL,
132 &manifest_memsink);
133 download::Failures retval = download_manager()->Fetch(&download_manifest);
134 if (retval != download::kFailOk) {
135 LogCvmfs(kLogCvmfs, kLogStderr, "failed to download manifest (%d - %s)",
136 retval, download::Code2Ascii(retval));
137 return 1;
138 }
139
140 manifest = manifest::Manifest::LoadMem(manifest_memsink.data(),
141 manifest_memsink.pos());
142 } else {
143 if (chdir(repository.c_str()) != 0) {
144 LogCvmfs(kLogCvmfs, kLogStderr, "failed to switch to directory %s",
145 repository.c_str());
146 return 1;
147 }
148 manifest = manifest::Manifest::LoadFile(".cvmfspublished");
149 }
150
151 if (!manifest.IsValid()) {
152 LogCvmfs(kLogCvmfs, kLogStderr, "failed to load repository manifest");
153 return 1;
154 }
155
156 // Validate Manifest
157 const string certificate_path = "data/" + manifest->certificate().MakePath();
158 if (!Exists(repository, certificate_path)) {
159 LogCvmfs(kLogCvmfs, kLogStderr, "failed to find certificate (%s)",
160 certificate_path.c_str());
161 return 1;
162 }
163
164 // Get information from the mount point
165 if (args.count('C') > 0) {
166 assert(!mount_point.empty());
167 const std::string root_hash_xattr = "user.root_hash";
168 std::string root_hash;
169 const bool success =
170 platform_getxattr(mount_point, root_hash_xattr, &root_hash);
171 if (!success) {
172 LogCvmfs(kLogCvmfs, kLogStderr,
173 "failed to retrieve extended attribute "
174 " '%s' from '%s' (errno: %d)",
175 root_hash_xattr.c_str(), mount_point.c_str(), errno);
176 return 1;
177 }
178 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
179 (human_readable) ? "Mounted Root Hash: " : "",
180 root_hash.c_str());
181 }
182
183 // Get information about external data
184 if (args.count('X') > 0) {
185 assert(!mount_point.empty());
186 const std::string external_data_xattr = "user.external_data";
187 std::string external_data;
188 const bool success =
189 platform_getxattr(mount_point, external_data_xattr, &external_data);
190 if (!success) {
191 LogCvmfs(kLogCvmfs, kLogStderr,
192 "failed to retrieve extended attribute "
193 " '%s' from '%s' (errno: %d)",
194 external_data_xattr.c_str(), mount_point.c_str(), errno);
195 return 1;
196 }
197 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
198 (human_readable) ? "External data enabled: " : "",
199 external_data.c_str());
200 }
201
202 // Get information from the Manifest
203 if (args.count('c') > 0) {
204 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
205 (human_readable) ? "Root Catalog Hash: " : "",
206 manifest->catalog_hash().ToString().c_str());
207 }
208
209 if (args.count('n') > 0) {
210 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
211 (human_readable) ? "Fully Qualified Repository Name: " : "",
212 manifest->repository_name().c_str());
213 }
214
215 if (args.count('t') > 0) {
216 LogCvmfs(kLogCvmfs, kLogStdout, "%s%lu",
217 (human_readable) ? "Time Stamp: " : "",
218 manifest->publish_timestamp());
219 }
220
221 if (args.count('m') > 0) {
222 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
223 (human_readable) ? "Replication Master Copy: " : "",
224 (Exists(repository, ".cvmfs_master_replica")) ? "true" : "false");
225 }
226
227 if (args.count('v') > 0) {
228 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
229 (human_readable) ? "Revision: " : "",
230 (StringifyInt(manifest->revision())).c_str());
231 }
232
233 if (args.count('g') > 0) {
234 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
235 (human_readable) ? "Garbage Collectable: " : "",
236 (StringifyBool(manifest->garbage_collectable())).c_str());
237 }
238
239 if (args.count('o') > 0) {
240 LogCvmfs(kLogCvmfs, kLogStdout, "%s%s",
241 (human_readable) ? "Maintains Reference Log: " : "",
242 (Exists(repository, ".cvmfsreflog")) ? "true" : "false");
243 }
244
245 if (args.count('M') > 0) {
246 shash::Any meta_info(manifest->meta_info());
247 if (meta_info.IsNull()) {
248 if (human_readable)
249 LogCvmfs(kLogCvmfs, kLogStderr, "no meta info available");
250 return 0;
251 }
252 const string url = repository + "/data/" + meta_info.MakePath();
253 cvmfs::MemSink metainfo_memsink;
254 download::JobInfo download_metainfo(&url, true, false, &meta_info,
255 &metainfo_memsink);
256 download::Failures retval = download_manager()->Fetch(&download_metainfo);
257 if (retval != download::kFailOk) {
258 if (human_readable)
259 LogCvmfs(kLogCvmfs, kLogStderr,
260 "failed to download meta info (%d - %s)", retval,
261 download::Code2Ascii(retval));
262 return 1;
263 }
264 string info(reinterpret_cast<char*>(metainfo_memsink.data()),
265 metainfo_memsink.pos());
266 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "%s", info.c_str());
267 }
268
269 if (args.count('R') > 0) {
270 LogCvmfs(kLogCvmfs, kLogStdout | kLogNoLinebreak, "%s",
271 manifest->ExportString().c_str());
272 }
273
274 return 0;
275 }
276
277 //------------------------------------------------------------------------------
278
279 int CommandVersion::Main(const ArgumentList &args) {
280 LogCvmfs(kLogCvmfs, kLogStdout, "%s", PACKAGE_VERSION);
281 return 0;
282 }
283
284 } // namespace swissknife
285