VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTManifest.cpp@ 106877

Last change on this file since 106877 was 106061, checked in by vboxsync, 3 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.3 KB
Line 
1/* $Id: RTManifest.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Manifest Utility.
4 */
5
6/*
7 * Copyright (C) 2010-2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/manifest.h>
42
43#include <iprt/buildconfig.h>
44#include <iprt/errcore.h>
45#include <iprt/file.h>
46#include <iprt/getopt.h>
47#include <iprt/initterm.h>
48#include <iprt/message.h>
49#include <iprt/path.h>
50#include <iprt/process.h>
51#include <iprt/stream.h>
52#include <iprt/string.h>
53#include <iprt/vfs.h>
54
55
56/**
57 * Verify a manifest.
58 *
59 * @returns Program exit code, failures with error message.
60 * @param pszManifest The manifest file. NULL if standard input.
61 * @param fStdFormat Whether to expect standard format (true) or
62 * java format (false).
63 * @param pszChDir The directory to change into before processing
64 * the files in the manifest.
65 */
66static RTEXITCODE rtManifestDoVerify(const char *pszManifest, bool fStdFormat, const char *pszChDir)
67{
68 RT_NOREF_PV(pszChDir); /** @todo implement pszChDir! */
69
70 /*
71 * Open the manifest.
72 */
73 int rc;
74 RTVFSIOSTREAM hVfsIos;
75 if (!pszManifest)
76 {
77 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_INPUT, RTFILE_O_READ, false /*fLeaveOpen*/, &hVfsIos);
78 if (RT_FAILURE(rc))
79 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard input for reading: %Rrc", rc);
80 }
81 else
82 {
83 uint32_t offError = 0;
84 RTERRINFOSTATIC ErrInfo;
85 rc = RTVfsChainOpenIoStream(pszManifest, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
86 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
87 if (RT_FAILURE(rc))
88 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszManifest, rc, offError, &ErrInfo.Core);
89 }
90
91 /*
92 * Read it.
93 */
94 RTMANIFEST hManifest;
95 rc = RTManifestCreate(0 /*fFlags*/, &hManifest);
96 if (RT_SUCCESS(rc))
97 {
98 if (fStdFormat)
99 {
100 char szErr[4096 + 1024];
101 rc = RTManifestReadStandardEx(hManifest, hVfsIos, szErr, sizeof(szErr));
102 if (RT_SUCCESS(rc))
103 {
104 RTVfsIoStrmRelease(hVfsIos);
105 hVfsIos = NIL_RTVFSIOSTREAM;
106
107 /*
108 * Do the verification.
109 */
110 /** @todo We're missing some enumeration APIs here! */
111 RTMsgError("The manifest read fine, but the actual verification code is yet to be written. Sorry.");
112 rc = VERR_NOT_IMPLEMENTED;
113#if 1 /* For now, just write the manifest to stdout so we can test the read routine. */
114 RTVFSIOSTREAM hVfsIosOut;
115 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, false /*fLeaveOpen*/, &hVfsIosOut);
116 if (RT_SUCCESS(rc))
117 {
118 RTManifestWriteStandard(hManifest, hVfsIosOut);
119 RTVfsIoStrmRelease(hVfsIosOut);
120 }
121#endif
122 }
123 else if (szErr[0])
124 RTMsgError("Error reading manifest: %s", szErr);
125 else
126 RTMsgError("Error reading manifest: %Rrc", rc);
127 }
128 else
129 {
130 RTMsgError("Support for Java manifest files is not implemented yet");
131 rc = VERR_NOT_IMPLEMENTED;
132 }
133 RTManifestRelease(hManifest);
134 }
135
136 RTVfsIoStrmRelease(hVfsIos);
137 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
138}
139
140
141/**
142 * Adds a file to the manifest.
143 *
144 * @returns IPRT status code, failures with error message.
145 * @param hManifest The manifest to add it to.
146 * @param pszFilename The name of the file to add.
147 * @param fAttr The manifest attributes to add.
148 */
149static int rtManifestAddFileToManifest(RTMANIFEST hManifest, const char *pszFilename, uint32_t fAttr)
150{
151 RTVFSIOSTREAM hVfsIos;
152 uint32_t offError = 0;
153 RTERRINFOSTATIC ErrInfo;
154 int rc = RTVfsChainOpenIoStream(pszFilename, RTFILE_O_READ | RTFILE_O_DENY_WRITE | RTFILE_O_OPEN,
155 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
156 if (RT_FAILURE(rc))
157 {
158 RTVfsChainMsgError("RTVfsChainOpenIoStream", pszFilename, rc, offError, &ErrInfo.Core);
159 return rc;
160 }
161
162 rc = RTManifestEntryAddIoStream(hManifest, hVfsIos, pszFilename, fAttr);
163 if (RT_FAILURE(rc))
164 RTMsgError("RTManifestEntryAddIoStream failed for '%s': %Rrc", pszFilename, rc);
165
166 RTVfsIoStrmRelease(hVfsIos);
167 return rc;
168}
169
170
171/**
172 * Create a manifest from the specified input files.
173 *
174 * @returns Program exit code, failures with error message.
175 * @param pszManifest The name of the output manifest file. NULL if
176 * it should be written to standard output.
177 * @param fStdFormat Whether to expect standard format (true) or
178 * java format (false).
179 * @param pszChDir The directory to change into before processing
180 * the file arguments.
181 * @param fAttr The file attributes to put in the manifest.
182 * @param pGetState The RTGetOpt state.
183 * @param pUnion What the last RTGetOpt() call returned.
184 * @param chOpt What the last RTGetOpt() call returned.
185 */
186static RTEXITCODE rtManifestDoCreate(const char *pszManifest, bool fStdFormat, const char *pszChDir, uint32_t fAttr,
187 PRTGETOPTSTATE pGetState, PRTGETOPTUNION pUnion, int chOpt)
188{
189 /*
190 * Open the manifest file.
191 */
192 int rc;
193 RTVFSIOSTREAM hVfsIos;
194 if (!pszManifest)
195 {
196 rc = RTVfsIoStrmFromStdHandle(RTHANDLESTD_OUTPUT, RTFILE_O_WRITE, false /*fLeaveOpen*/, &hVfsIos);
197 if (RT_FAILURE(rc))
198 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to prepare standard output for writing: %Rrc", rc);
199 }
200 else
201 {
202 RTERRINFOSTATIC ErrInfo;
203 uint32_t offError;
204 rc = RTVfsChainOpenIoStream(pszManifest, RTFILE_O_WRITE | RTFILE_O_DENY_WRITE | RTFILE_O_CREATE_REPLACE,
205 &hVfsIos, &offError, RTErrInfoInitStatic(&ErrInfo));
206 if (RT_FAILURE(rc))
207 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenIoStream", pszManifest, rc, offError, &ErrInfo.Core);
208 }
209
210 /*
211 * Create the internal manifest.
212 */
213 RTMANIFEST hManifest;
214 rc = RTManifestCreate(0 /*fFlags*/, &hManifest);
215 if (RT_SUCCESS(rc))
216 {
217 /*
218 * Change directory and start processing the specified files.
219 */
220 if (pszChDir)
221 {
222 rc = RTPathSetCurrent(pszChDir);
223 if (RT_FAILURE(rc))
224 RTMsgError("Failed to change directory to '%s': %Rrc", pszChDir, rc);
225 }
226 if (RT_SUCCESS(rc))
227 {
228 while (chOpt == VINF_GETOPT_NOT_OPTION)
229 {
230 rc = rtManifestAddFileToManifest(hManifest, pUnion->psz, fAttr);
231 if (RT_FAILURE(rc))
232 break;
233
234 /* next */
235 chOpt = RTGetOpt(pGetState, pUnion);
236 }
237 if (RT_SUCCESS(rc) && chOpt != 0)
238 {
239 RTGetOptPrintError(chOpt, pUnion);
240 rc = chOpt < 0 ? chOpt : -chOpt;
241 }
242 }
243
244 /*
245 * Write the manifest.
246 */
247 if (RT_SUCCESS(rc))
248 {
249 if (fStdFormat)
250 {
251 rc = RTManifestWriteStandard(hManifest, hVfsIos);
252 if (RT_FAILURE(rc))
253 RTMsgError("RTManifestWriteStandard failed: %Rrc", rc);
254 }
255 else
256 {
257 RTMsgError("Support for Java manifest files is not implemented yet");
258 rc = VERR_NOT_IMPLEMENTED;
259 }
260 }
261
262 RTManifestRelease(hManifest);
263 }
264
265 RTVfsIoStrmRelease(hVfsIos);
266 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
267}
268
269
270int main(int argc, char **argv)
271{
272 int rc = RTR3InitExe(argc, &argv, 0);
273 if (RT_FAILURE(rc))
274 return RTMsgInitFailure(rc);
275
276 /*
277 * Parse arguments.
278 */
279 static RTGETOPTDEF const s_aOptions[] =
280 {
281 { "--manifest", 'm', RTGETOPT_REQ_STRING },
282 { "--java", 'j', RTGETOPT_REQ_NOTHING },
283 { "--chdir", 'C', RTGETOPT_REQ_STRING },
284 { "--attribute", 'a', RTGETOPT_REQ_STRING },
285 { "--verify", 'v', RTGETOPT_REQ_NOTHING },
286 };
287
288 bool fVerify = false;
289 bool fStdFormat = true;
290 const char *pszManifest = NULL;
291 const char *pszChDir = NULL;
292 uint32_t fAttr = RTMANIFEST_ATTR_UNKNOWN;
293
294 RTGETOPTSTATE GetState;
295 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
296 if (RT_FAILURE(rc))
297 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc", rc);
298
299 RTGETOPTUNION ValueUnion;
300 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
301 && rc != VINF_GETOPT_NOT_OPTION)
302 {
303 switch (rc)
304 {
305 case 'a':
306 {
307 static struct
308 {
309 const char *pszAttr;
310 uint32_t fAttr;
311 } s_aAttributes[] =
312 {
313 { "size", RTMANIFEST_ATTR_SIZE },
314 { "md5", RTMANIFEST_ATTR_MD5 },
315 { "sha1", RTMANIFEST_ATTR_SHA1 },
316 { "sha256", RTMANIFEST_ATTR_SHA256 },
317 { "sha512", RTMANIFEST_ATTR_SHA512 }
318 };
319 uint32_t fThisAttr = RTMANIFEST_ATTR_UNKNOWN;
320 for (unsigned i = 0; i < RT_ELEMENTS(s_aAttributes); i++)
321 if (!RTStrICmp(s_aAttributes[i].pszAttr, ValueUnion.psz))
322 {
323 fThisAttr = s_aAttributes[i].fAttr;
324 break;
325 }
326 if (fThisAttr == RTMANIFEST_ATTR_UNKNOWN)
327 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown attribute type '%s'", ValueUnion.psz);
328
329 if (fAttr == RTMANIFEST_ATTR_UNKNOWN)
330 fAttr = fThisAttr;
331 else
332 fAttr |= fThisAttr;
333 break;
334 }
335
336 case 'j':
337 fStdFormat = false;
338 break;
339
340 case 'm':
341 if (pszManifest)
342 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one manifest can be specified");
343 pszManifest = ValueUnion.psz;
344 break;
345
346 case 'v':
347 fVerify = true;
348 break;
349
350 case 'C':
351 if (pszChDir)
352 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Only one directory change can be specified");
353 pszChDir = ValueUnion.psz;
354 break;
355
356 case 'h':
357 RTPrintf("Usage: %s [--manifest <file>] [--chdir <dir>] [--attribute <attrib-name> [..]] <files>\n"
358 " or %s --verify [--manifest <file>] [--chdir <dir>]\n"
359 "\n"
360 "attrib-name: size, md5, sha1, sha256 or sha512\n"
361 , RTProcShortName(), RTProcShortName());
362 return RTEXITCODE_SUCCESS;
363
364#ifndef IN_BLD_PROG /* RTBldCfgVersion or RTBldCfgRevision in build time IPRT lib. */
365 case 'V':
366 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
367 return RTEXITCODE_SUCCESS;
368#endif
369
370 default:
371 return RTGetOptPrintError(rc, &ValueUnion);
372 }
373 }
374
375 /*
376 * Take action.
377 */
378 RTEXITCODE rcExit;
379 if (!fVerify)
380 {
381 if (rc != VINF_GETOPT_NOT_OPTION)
382 RTMsgWarning("No files specified, the manifest will be empty.");
383 if (fAttr == RTMANIFEST_ATTR_UNKNOWN)
384 fAttr = RTMANIFEST_ATTR_SIZE | RTMANIFEST_ATTR_MD5
385 | RTMANIFEST_ATTR_SHA1 | RTMANIFEST_ATTR_SHA256 | RTMANIFEST_ATTR_SHA512;
386 rcExit = rtManifestDoCreate(pszManifest, fStdFormat, pszChDir, fAttr, &GetState, &ValueUnion, rc);
387 }
388 else
389 {
390 if (rc == VINF_GETOPT_NOT_OPTION)
391 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
392 "No files should be specified when verifying a manifest (--verfiy), "
393 "only a manifest via the --manifest option");
394 if (fAttr != RTMANIFEST_ATTR_UNKNOWN)
395 return RTMsgErrorExit(RTEXITCODE_SYNTAX,
396 "The --attribute (-a) option does not combine with --verify (-v)");
397
398
399 rcExit = rtManifestDoVerify(pszManifest, fStdFormat, pszChDir);
400 }
401
402 return rcExit;
403}
404
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette