VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTMkDir.cpp

Last change on this file was 106061, checked in by vboxsync, 8 weeks ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.5 KB
Line 
1/* $Id: RTMkDir.cpp 106061 2024-09-16 14:03:52Z vboxsync $ */
2/** @file
3 * IPRT - Creates directory.
4 */
5
6/*
7 * Copyright (C) 2013-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/path.h>
42#include <iprt/err.h>
43#include <iprt/initterm.h>
44#include <iprt/message.h>
45
46#include <iprt/vfs.h>
47#include <iprt/string.h>
48#include <iprt/stream.h>
49#include <iprt/getopt.h>
50#include <iprt/buildconfig.h>
51
52
53/*********************************************************************************************************************************
54* Structures and Typedefs *
55*********************************************************************************************************************************/
56typedef struct RTCMDMKDIROPTS
57{
58 /** -v, --verbose */
59 bool fVerbose;
60 /** -p, --parents */
61 bool fParents;
62 /** Whether to always use the VFS chain API (for testing). */
63 bool fAlwaysUseChainApi;
64 /** Directory creation flags (RTDIRCREATE_FLAGS_XXX). */
65 uint32_t fCreateFlags;
66 /** The directory mode. */
67 RTFMODE fMode;
68} RTCMDMKDIROPTS;
69
70
71/**
72 * Create one directory and any missing parent directories.
73 *
74 * @returns exit code
75 * @param pOpts The mkdir option.
76 * @param pszDir The path to the new directory.
77 */
78static int rtCmdMkDirOneWithParents(RTCMDMKDIROPTS const *pOpts, const char *pszDir)
79{
80 int rc;
81 if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszDir) )
82 {
83 /*
84 * Use the API for doing the entire job. Unfortuantely, this means we
85 * can't be very verbose about what we're doing.
86 */
87 rc = RTDirCreateFullPath(pszDir, pOpts->fMode);
88 if (RT_FAILURE(rc))
89 RTMsgError("Failed to create directory '%s' (or a parent): %Rrc", pszDir, rc);
90 else if (pOpts->fVerbose)
91 RTPrintf("%s\n", pszDir);
92 }
93 else
94 {
95 /*
96 * Strip the final path element from the pszDir spec.
97 */
98 char *pszCopy = RTStrDup(pszDir);
99 if (!pszCopy)
100 return RTMsgErrorExitFailure("Out of string memory!");
101
102 char *pszFinalPath;
103 char *pszSpec;
104 uint32_t offError;
105 rc = RTVfsChainSplitOffFinalPath(pszCopy, &pszSpec, &pszFinalPath, &offError);
106 if (RT_SUCCESS(rc))
107 {
108 const char * const pszFullFinalPath = pszFinalPath;
109
110 /*
111 * Open the root director/whatever.
112 */
113 RTERRINFOSTATIC ErrInfo;
114 RTVFSDIR hVfsCurDir;
115 if (pszSpec)
116 {
117 rc = RTVfsChainOpenDir(pszSpec, 0 /*fOpen*/, &hVfsCurDir, &offError, RTErrInfoInitStatic(&ErrInfo));
118 if (RT_FAILURE(rc))
119 RTVfsChainMsgError("RTVfsChainOpenDir", pszSpec, rc, offError, &ErrInfo.Core);
120 else if (!pszFinalPath)
121 pszFinalPath = RTStrEnd(pszSpec, RTSTR_MAX);
122 }
123 else if (!RTPathStartsWithRoot(pszFinalPath))
124 {
125 rc = RTVfsDirOpenNormal(".", 0 /*fOpen*/, &hVfsCurDir);
126 if (RT_FAILURE(rc))
127 RTMsgError("Failed to open '.' (for %s): %Rrc", rc, pszFinalPath);
128 }
129 else
130 {
131 char *pszRoot = pszFinalPath;
132 pszFinalPath = RTPathSkipRootSpec(pszFinalPath);
133 char const chSaved = *pszFinalPath;
134 *pszFinalPath = '\0';
135 rc = RTVfsDirOpenNormal(pszRoot, 0 /*fOpen*/, &hVfsCurDir);
136 *pszFinalPath = chSaved;
137 if (RT_FAILURE(rc))
138 RTMsgError("Failed to open root dir for '%s': %Rrc", rc, pszRoot);
139 }
140
141 /*
142 * Walk the path component by component.
143 */
144 while (RT_SUCCESS(rc))
145 {
146 /*
147 * Strip leading slashes.
148 */
149 while (RTPATH_IS_SLASH(*pszFinalPath))
150 pszFinalPath++;
151 if (*pszFinalPath == '\0')
152 {
153 RTVfsDirRelease(hVfsCurDir);
154 break;
155 }
156
157 /*
158 * Find the end of the next path component.
159 */
160 size_t cchComponent = 0;
161 char ch;
162 while ( (ch = pszFinalPath[cchComponent]) != '\0'
163 && !RTPATH_IS_SLASH(ch))
164 cchComponent++;
165
166 /*
167 * Open or create the component.
168 */
169 pszFinalPath[cchComponent] = '\0';
170 RTVFSDIR hVfsNextDir = NIL_RTVFSDIR;
171 for (uint32_t cTries = 0; cTries < 8; cTries++)
172 {
173 /* Try open it. */
174 rc = RTVfsDirOpenDir(hVfsCurDir, pszFinalPath, 0 /*fFlags*/, &hVfsNextDir);
175 if (RT_SUCCESS(rc))
176 break;
177 if ( rc != VERR_FILE_NOT_FOUND
178 && rc != VERR_PATH_NOT_FOUND)
179 {
180 if (ch == '\0')
181 RTMsgError("Failed opening directory '%s': %Rrc", pszDir, rc);
182 else
183 RTMsgError("Failed opening dir '%s' (for creating '%s'): %Rrc", pszFullFinalPath, pszDir, rc);
184 break;
185 }
186
187 /* Not found, so try create it. */
188 rc = RTVfsDirCreateDir(hVfsCurDir, pszFinalPath, pOpts->fMode, pOpts->fCreateFlags, &hVfsNextDir);
189 if (rc == VERR_ALREADY_EXISTS)
190 continue; /* We lost a creation race, try again. */
191 if (RT_SUCCESS(rc) && pOpts->fVerbose)
192 {
193 if (pszSpec)
194 RTPrintf("%s:%s\n", pszSpec, pszFullFinalPath);
195 else
196 RTPrintf("%s\n", pszFullFinalPath);
197 }
198 else if (RT_FAILURE(rc))
199 {
200 if (ch == '\0')
201 RTMsgError("Failed creating directory '%s': %Rrc", pszDir, rc);
202 else
203 RTMsgError("Failed creating dir '%s' (for '%s'): %Rrc", pszFullFinalPath, pszDir, rc);
204 }
205 break;
206 }
207 pszFinalPath[cchComponent] = ch;
208
209 RTVfsDirRelease(hVfsCurDir);
210 hVfsCurDir = hVfsNextDir;
211 pszFinalPath += cchComponent;
212 }
213 }
214 else
215 RTVfsChainMsgError("RTVfsChainOpenParentDir", pszCopy, rc, offError, NULL);
216 RTStrFree(pszCopy);
217 }
218 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
219}
220
221
222/**
223 * Create one directory.
224 *
225 * @returns exit code
226 * @param pOpts The mkdir option.
227 * @param pszDir The path to the new directory.
228 */
229static RTEXITCODE rtCmdMkDirOne(RTCMDMKDIROPTS const *pOpts, const char *pszDir)
230{
231 int rc;
232 if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszDir) )
233 rc = RTDirCreate(pszDir, pOpts->fMode, 0);
234 else
235 {
236 RTVFSDIR hVfsDir;
237 const char *pszChild;
238 uint32_t offError;
239 RTERRINFOSTATIC ErrInfo;
240 rc = RTVfsChainOpenParentDir(pszDir, 0 /*fOpen*/, &hVfsDir, &pszChild, &offError, RTErrInfoInitStatic(&ErrInfo));
241 if (RT_SUCCESS(rc))
242 {
243 rc = RTVfsDirCreateDir(hVfsDir, pszChild, pOpts->fMode, 0 /*fFlags*/, NULL);
244 RTVfsDirRelease(hVfsDir);
245 }
246 else
247 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenParentDir", pszDir, rc, offError, &ErrInfo.Core);
248 }
249 if (RT_SUCCESS(rc))
250 {
251 if (pOpts->fVerbose)
252 RTPrintf("%s\n", pszDir);
253 return RTEXITCODE_SUCCESS;
254 }
255 return RTMsgErrorExitFailure("Failed to create '%s': %Rrc", pszDir, rc);
256}
257
258
259static RTEXITCODE RTCmdMkDir(unsigned cArgs, char **papszArgs)
260{
261 /*
262 * Parse the command line.
263 */
264 static const RTGETOPTDEF s_aOptions[] =
265 {
266 /* operations */
267 { "--mode", 'm', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
268 { "--parents", 'p', RTGETOPT_REQ_NOTHING },
269 { "--always-use-vfs-chain-api", 'A', RTGETOPT_REQ_NOTHING },
270 { "--allow-content-indexing", 'i', RTGETOPT_REQ_NOTHING },
271 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
272 };
273
274 RTGETOPTSTATE GetState;
275 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
276 RTGETOPTINIT_FLAGS_OPTS_FIRST);
277 if (RT_FAILURE(rc))
278 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
279
280 RTCMDMKDIROPTS Opts;
281 Opts.fVerbose = false;
282 Opts.fParents = false;
283 Opts.fAlwaysUseChainApi = false;
284 Opts.fCreateFlags = RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET;
285 Opts.fMode = 0775 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
286
287 RTGETOPTUNION ValueUnion;
288 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
289 && rc != VINF_GETOPT_NOT_OPTION)
290 {
291 switch (rc)
292 {
293 case 'm':
294 /** @todo DOS+NT attributes and symbolic notation. */
295 Opts.fMode &= ~07777;
296 Opts.fMode |= ValueUnion.u32 & 07777;
297 break;
298
299 case 'p':
300 Opts.fParents = true;
301 break;
302
303 case 'v':
304 Opts.fVerbose = true;
305 break;
306
307 case 'A':
308 Opts.fAlwaysUseChainApi = true;
309 break;
310
311 case 'i':
312 Opts.fCreateFlags &= ~RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET;
313 Opts.fCreateFlags |= RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET;
314 break;
315
316 case 'h':
317 RTPrintf("Usage: %s [options] <dir> [..]\n"
318 "\n"
319 "Options:\n"
320 " -m <mode>, --mode <mode>\n"
321 " The creation mode. Default is 0775.\n"
322 " -p, --parent\n"
323 " Create parent directories too. Ignore any existing directories.\n"
324 " -v, --verbose\n"
325 " Tell which directories get created.\n"
326 " -A, --always-use-vfs-chain-api\n"
327 " Always use the VFS API.\n"
328 " -i, --allow-content-indexing\n"
329 " Don't set flags to disable context indexing on windows.\n"
330 , papszArgs[0]);
331 return RTEXITCODE_SUCCESS;
332
333 case 'V':
334 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
335 return RTEXITCODE_SUCCESS;
336
337 default:
338 return RTGetOptPrintError(rc, &ValueUnion);
339 }
340 }
341
342
343 /*
344 * No files means error.
345 */
346 if (rc != VINF_GETOPT_NOT_OPTION)
347 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No directories specified.\n");
348
349 /*
350 * Work thru the specified dirs.
351 */
352 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
353 while (rc == VINF_GETOPT_NOT_OPTION)
354 {
355 if (Opts.fParents)
356 rc = rtCmdMkDirOneWithParents(&Opts, ValueUnion.psz);
357 else
358 rc = rtCmdMkDirOne(&Opts, ValueUnion.psz);
359 if (RT_FAILURE(rc))
360 rcExit = RTEXITCODE_FAILURE;
361
362 /* next */
363 rc = RTGetOpt(&GetState, &ValueUnion);
364 }
365 if (rc != 0)
366 rcExit = RTGetOptPrintError(rc, &ValueUnion);
367
368 return rcExit;
369}
370
371
372int main(int argc, char **argv)
373{
374 int rc = RTR3InitExe(argc, &argv, 0);
375 if (RT_FAILURE(rc))
376 return RTMsgInitFailure(rc);
377 return RTCmdMkDir(argc, argv);
378}
379
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