VirtualBox

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

Last change on this file since 39419 was 39181, checked in by vboxsync, 13 years ago

RTManifest.cpp: Fixed exit code mixup causing file reading errors to be ignored.

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