VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTChMod.cpp@ 93115

Last change on this file since 93115 was 93115, checked in by vboxsync, 3 years ago

scm --update-copyright-year

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.5 KB
Line 
1/* $Id: RTChMod.cpp 93115 2022-01-01 11:31:46Z vboxsync $ */
2/** @file
3 * IPRT - Changes the mode/attributes of a file system object.
4 */
5
6/*
7 * Copyright (C) 2013-2022 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/buildconfig.h>
32#include <iprt/errcore.h>
33#include <iprt/file.h>
34#include <iprt/getopt.h>
35#include <iprt/initterm.h>
36#include <iprt/message.h>
37#include <iprt/path.h>
38#include <iprt/stream.h>
39#include <iprt/string.h>
40#include <iprt/vfs.h>
41
42
43/*********************************************************************************************************************************
44* Defined Constants And Macros *
45*********************************************************************************************************************************/
46/** What to clear we all bits are being set. */
47#define RTCHMOD_SET_ALL_MASK (~( RTFS_TYPE_MASK \
48 | RTFS_DOS_NT_ENCRYPTED \
49 | RTFS_DOS_NT_COMPRESSED \
50 | RTFS_DOS_NT_REPARSE_POINT \
51 | RTFS_DOS_NT_SPARSE_FILE \
52 | RTFS_DOS_NT_DEVICE \
53 | RTFS_DOS_DIRECTORY))
54
55
56/*********************************************************************************************************************************
57* Structures and Typedefs *
58*********************************************************************************************************************************/
59typedef enum RTCMDCHMODNOISE
60{
61 kRTCmdChModNoise_Quiet,
62 kRTCmdChModNoise_Default,
63 kRTCmdChModNoise_Changes,
64 kRTCmdChModNoise_Verbose
65} RTCMDCHMODNOISE;
66
67
68typedef struct RTCMDCHMODOPTS
69{
70 /** The noise level. */
71 RTCMDCHMODNOISE enmNoiseLevel;
72 /** -R, --recursive */
73 bool fRecursive;
74 /** --preserve-root / --no-preserve-root (don't allow recursion from root). */
75 bool fPreserveRoot;
76 /** Whether to always use the VFS chain API (for testing). */
77 bool fAlwaysUseChainApi;
78 /** Which mode bits to set. */
79 RTFMODE fModeSet;
80 /** Which mode bits to clear. */
81 RTFMODE fModeClear;
82} RTCMDCHMODOPTS;
83
84
85
86/**
87 * Calculates the new file mode.
88 *
89 * @returns New mode mask.
90 * @param pOpts The chmod options.
91 * @param fMode The current file mode.
92 */
93static RTFMODE rtCmdMkModCalcNewMode(RTCMDCHMODOPTS const *pOpts, RTFMODE fMode)
94{
95 fMode &= ~pOpts->fModeClear;
96 fMode |= pOpts->fModeSet;
97 /** @todo do 'X' */
98 return fMode;
99}
100
101
102/**
103 * Changes the file mode of one file system object.
104 *
105 * @returns exit code
106 * @param pOpts The chmod options.
107 * @param pszPath The path to the file system object to change the
108 * file mode of.
109 */
110static RTEXITCODE rtCmdChModOne(RTCMDCHMODOPTS const *pOpts, const char *pszPath)
111{
112 int rc;
113 RTFSOBJINFO ObjInfo;
114 bool fChanges = false;
115 if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszPath) )
116 {
117 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
118 if (RT_SUCCESS(rc))
119 {
120 RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode);
121 fChanges = fNewMode != ObjInfo.Attr.fMode;
122 if (fChanges)
123 {
124 rc = RTPathSetMode(pszPath, fNewMode);
125 if (RT_FAILURE(rc))
126 RTMsgError("RTPathSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc);
127 }
128 }
129 else
130 RTMsgError("RTPathQueryInfoEx failed on '%s': %Rrc", pszPath, rc);
131 }
132 else
133 {
134 RTVFSOBJ hVfsObj;
135 uint32_t offError;
136 RTERRINFOSTATIC ErrInfo;
137 rc = RTVfsChainOpenObj(pszPath, RTFILE_O_ACCESS_ATTR_READWRITE | RTFILE_O_DENY_NONE | RTFILE_O_OPEN,
138 RTVFSOBJ_F_OPEN_ANY | RTVFSOBJ_F_CREATE_NOTHING | RTPATH_F_FOLLOW_LINK,
139 &hVfsObj, &offError, RTErrInfoInitStatic(&ErrInfo));
140 if (RT_SUCCESS(rc))
141 {
142 rc = RTVfsObjQueryInfo(hVfsObj, &ObjInfo, RTFSOBJATTRADD_NOTHING);
143 if (RT_SUCCESS(rc))
144 {
145 RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode);
146 fChanges = fNewMode != ObjInfo.Attr.fMode;
147 if (fChanges)
148 {
149 rc = RTVfsObjSetMode(hVfsObj, fNewMode, RTCHMOD_SET_ALL_MASK);
150 if (RT_FAILURE(rc))
151 RTMsgError("RTVfsObjSetMode failed on '%s' with fNewMode=%#x: %Rrc", pszPath, fNewMode, rc);
152 }
153 }
154 else
155 RTVfsChainMsgError("RTVfsObjQueryInfo", pszPath, rc, offError, &ErrInfo.Core);
156 RTVfsObjRelease(hVfsObj);
157 }
158 else
159 RTVfsChainMsgError("RTVfsChainOpenObject", pszPath, rc, offError, &ErrInfo.Core);
160 }
161
162 if (RT_SUCCESS(rc))
163 {
164 if (pOpts->enmNoiseLevel >= (fChanges ? kRTCmdChModNoise_Changes : kRTCmdChModNoise_Verbose))
165 RTPrintf("%s\n", pszPath);
166 return RTEXITCODE_SUCCESS;
167 }
168 return RTEXITCODE_FAILURE;
169}
170
171
172/**
173 * Recursively changes the file mode.
174 *
175 * @returns exit code
176 * @param pOpts The mkdir option.
177 * @param pszPath The path to start changing the mode of.
178 */
179static int rtCmdChModRecursive(RTCMDCHMODOPTS const *pOpts, const char *pszPath)
180{
181 /*
182 * Check if it's a directory first. If not, join the non-recursive code.
183 */
184 int rc;
185 uint32_t offError;
186 RTFSOBJINFO ObjInfo;
187 RTERRINFOSTATIC ErrInfo;
188 bool const fUseChainApi = pOpts->fAlwaysUseChainApi || RTVfsChainIsSpec(pszPath);
189 if (!fUseChainApi)
190 {
191 rc = RTPathQueryInfoEx(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK);
192 if (RT_FAILURE(rc))
193 return RTMsgErrorExitFailure("RTPathQueryInfoEx failed on '%s': %Rrc", pszPath, rc);
194 }
195 else
196 {
197 rc = RTVfsChainQueryInfo(pszPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK,
198 &offError, RTErrInfoInitStatic(&ErrInfo));
199 if (RT_FAILURE(rc))
200 return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszPath, rc, offError, &ErrInfo.Core);
201 }
202
203 if (!RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
204 {
205 /*
206 * Don't bother redoing the above work if its not necessary.
207 */
208 RTFMODE fNewMode = rtCmdMkModCalcNewMode(pOpts, ObjInfo.Attr.fMode);
209 if (fNewMode != ObjInfo.Attr.fMode)
210 return rtCmdChModOne(pOpts, pszPath);
211 if (pOpts->enmNoiseLevel >= kRTCmdChModNoise_Verbose)
212 RTPrintf("%s\n", pszPath);
213 return RTEXITCODE_SUCCESS;
214 }
215
216 /*
217 * For recursion we always use the VFS layer.
218 */
219 RTVFSDIR hVfsDir;
220 if (!fUseChainApi)
221 {
222 rc = RTVfsDirOpenNormal(pszPath, 0 /** @todo write attrib flag*/, &hVfsDir);
223 if (RT_FAILURE(rc))
224 return RTMsgErrorExitFailure("RTVfsDirOpenNormal failed on '%s': %Rrc", pszPath, rc);
225 }
226 else
227 {
228 rc = RTVfsChainOpenDir(pszPath, 0 /** @todo write attrib flag*/, &hVfsDir, &offError, RTErrInfoInitStatic(&ErrInfo));
229 if (RT_FAILURE(rc))
230 return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszPath, rc, offError, &ErrInfo.Core);
231 }
232
233 RTMsgError("Recursion is not yet implemented\n");
234 RTVfsDirRelease(hVfsDir);
235 rc = VERR_NOT_IMPLEMENTED;
236
237 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
238}
239
240
241static RTEXITCODE RTCmdChMod(unsigned cArgs, char **papszArgs)
242{
243 /*
244 * Parse the command line.
245 */
246 static const RTGETOPTDEF s_aOptions[] =
247 {
248 /* operations */
249 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
250 { "--preserve-root", 'x', RTGETOPT_REQ_NOTHING },
251 { "--no-preserve-root", 'X', RTGETOPT_REQ_NOTHING },
252 { "--changes", 'c', RTGETOPT_REQ_NOTHING },
253 { "--quiet", 'f', RTGETOPT_REQ_NOTHING },
254 { "--silent", 'f', RTGETOPT_REQ_NOTHING },
255 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
256 { "--reference", 'Z', RTGETOPT_REQ_NOTHING },
257 { "--always-use-vfs-chain-api", 'A', RTGETOPT_REQ_NOTHING },
258
259 };
260
261 RTGETOPTSTATE GetState;
262 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
263 RTGETOPTINIT_FLAGS_OPTS_FIRST);
264 if (RT_FAILURE(rc))
265 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
266
267 RTCMDCHMODOPTS Opts;
268 Opts.enmNoiseLevel = kRTCmdChModNoise_Default;
269 Opts.fPreserveRoot = false;
270 Opts.fRecursive = false;
271 Opts.fAlwaysUseChainApi = false;
272 Opts.fModeClear = 0;
273 Opts.fModeSet = 0;
274
275 RTGETOPTUNION ValueUnion;
276 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
277 && rc != VINF_GETOPT_NOT_OPTION)
278 {
279 switch (rc)
280 {
281 case 'R':
282 Opts.fRecursive = true;
283 break;
284
285 case 'x':
286 Opts.fPreserveRoot = true;
287 break;
288 case 'X':
289 Opts.fPreserveRoot = false;
290 break;
291
292 case 'f':
293 Opts.enmNoiseLevel = kRTCmdChModNoise_Quiet;
294 break;
295 case 'c':
296 Opts.enmNoiseLevel = kRTCmdChModNoise_Changes;
297 break;
298 case 'v':
299 Opts.enmNoiseLevel = kRTCmdChModNoise_Verbose;
300 break;
301
302 case 'Z':
303 {
304 RTFSOBJINFO ObjInfo;
305 RTERRINFOSTATIC ErrInfo;
306 uint32_t offError;
307 rc = RTVfsChainQueryInfo(ValueUnion.psz, &ObjInfo,RTFSOBJATTRADD_NOTHING, RTPATH_F_FOLLOW_LINK,
308 &offError, RTErrInfoInitStatic(&ErrInfo));
309 if (RT_FAILURE(rc))
310 return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", ValueUnion.psz, rc, offError, &ErrInfo.Core);
311 Opts.fModeClear = RTCHMOD_SET_ALL_MASK;
312 Opts.fModeSet = ObjInfo.Attr.fMode & RTCHMOD_SET_ALL_MASK;
313 break;
314 }
315
316 case 'A':
317 Opts.fAlwaysUseChainApi = true;
318 break;
319
320 case 'h':
321 RTPrintf("Usage: %s [options] <mode> <file> [..]\n"
322 "\n"
323 "Options:\n"
324 " -f, --silent, --quiet\n"
325 " -c, --changes\n"
326 " -v, --verbose\n"
327 " Noise level selection.\n"
328 " -R, --recursive\n"
329 " Recurse into directories.\n"
330 " --preserve-root, --no-preserve-root\n"
331 " Whether to allow recursion from the root (default: yes).\n"
332 " --reference <file>\n"
333 " Take mode mask to use from <file> instead of <mode>.\n"
334 "\n"
335 "The <mode> part isn't fully implemented, so only numerical octal notation\n"
336 "works. Prefix the number(s) with 0x to use hexadecimal. There are two forms\n"
337 "of the numerical notation: <SET> and <SET>:<CLEAR>\n"
338 , papszArgs[0]);
339 return RTEXITCODE_SUCCESS;
340
341 case 'V':
342 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
343 return RTEXITCODE_SUCCESS;
344
345 default:
346
347 return RTGetOptPrintError(rc, &ValueUnion);
348 }
349 }
350
351 /*
352 * The MODE.
353 */
354 if ( Opts.fModeClear == 0
355 && Opts.fModeSet == 0)
356 {
357 if (rc != VINF_GETOPT_NOT_OPTION)
358 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No mode change specified.\n");
359
360 char *pszNext;
361 if ( ValueUnion.psz[0] == '0'
362 && (ValueUnion.psz[1] == 'x' || ValueUnion.psz[1] == 'X'))
363 rc = RTStrToUInt32Ex(ValueUnion.psz, &pszNext, 16, &Opts.fModeSet);
364 else
365 rc = RTStrToUInt32Ex(ValueUnion.psz, &pszNext, 8, &Opts.fModeSet);
366 if ( rc != VINF_SUCCESS
367 && (rc != VWRN_TRAILING_CHARS || *pszNext != ':'))
368 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unable to parse mode mask: %s\n", ValueUnion.psz);
369 Opts.fModeSet &= RTCHMOD_SET_ALL_MASK;
370
371 if (rc == VINF_SUCCESS)
372 Opts.fModeClear = RTCHMOD_SET_ALL_MASK;
373 else
374 {
375 pszNext++;
376 if ( pszNext[0] == '0'
377 && (pszNext[1] == 'x' || pszNext[1] == 'X'))
378 rc = RTStrToUInt32Ex(pszNext, &pszNext, 16, &Opts.fModeClear);
379 else
380 rc = RTStrToUInt32Ex(pszNext, &pszNext, 8, &Opts.fModeClear);
381 if (rc != VINF_SUCCESS)
382 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unable to parse mode mask: %s\n", ValueUnion.psz);
383 Opts.fModeClear &= RTCHMOD_SET_ALL_MASK;
384 }
385
386 rc = RTGetOpt(&GetState, &ValueUnion);
387 }
388
389 /*
390 * No files means error.
391 */
392 if (rc != VINF_GETOPT_NOT_OPTION)
393 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No directories specified.\n");
394
395 /*
396 * Work thru the specified dirs.
397 */
398 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
399 while (rc == VINF_GETOPT_NOT_OPTION)
400 {
401 if (Opts.fRecursive)
402 rc = rtCmdChModRecursive(&Opts, ValueUnion.psz);
403 else
404 rc = rtCmdChModOne(&Opts, ValueUnion.psz);
405 if (RT_FAILURE(rc))
406 rcExit = RTEXITCODE_FAILURE;
407
408 /* next */
409 rc = RTGetOpt(&GetState, &ValueUnion);
410 }
411 if (rc != 0)
412 rcExit = RTGetOptPrintError(rc, &ValueUnion);
413
414 return rcExit;
415}
416
417
418int main(int argc, char **argv)
419{
420 int rc = RTR3InitExe(argc, &argv, 0);
421 if (RT_FAILURE(rc))
422 return RTMsgInitFailure(rc);
423 return RTCmdChMod(argc, argv);
424}
425
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