VirtualBox

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

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