VirtualBox

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

Last change on this file since 69718 was 69718, checked in by vboxsync, 8 years ago

RTMkDir,nt/RTDirRelDirCreate: Some adjustments.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.1 KB
Line 
1/* $Id: RTMkDir.cpp 69718 2017-11-16 14:55:45Z vboxsync $ */
2/** @file
3 * IPRT - Creates directory.
4 */
5
6/*
7 * Copyright (C) 2013-2017 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 }
111 else if (!RTPathStartsWithRoot(pszFinalPath))
112 {
113 rc = RTVfsDirOpenNormal(".", 0 /*fOpen*/, &hVfsCurDir);
114 if (RT_FAILURE(rc))
115 RTMsgError("Failed to open '.' (for %s): %Rrc", rc, pszFinalPath);
116 }
117 else
118 {
119 char *pszRoot = pszFinalPath;
120 pszFinalPath = RTPathSkipRootSpec(pszFinalPath);
121 char const chSaved = *pszFinalPath;
122 *pszFinalPath = '\0';
123 rc = RTVfsDirOpenNormal(pszRoot, 0 /*fOpen*/, &hVfsCurDir);
124 *pszFinalPath = chSaved;
125 if (RT_FAILURE(rc))
126 RTMsgError("Failed to open root dir for '%s': %Rrc", rc, pszRoot);
127 }
128
129 /*
130 * Walk the path component by component.
131 */
132 while (RT_SUCCESS(rc))
133 {
134 /*
135 * Strip leading slashes.
136 */
137 while (RTPATH_IS_SLASH(*pszFinalPath))
138 pszFinalPath++;
139 if (*pszFinalPath == '\0')
140 {
141 RTVfsDirRelease(hVfsCurDir);
142 break;
143 }
144
145 /*
146 * Find the end of the next path component.
147 */
148 size_t cchComponent = 0;
149 char ch;
150 while ( (ch = pszFinalPath[cchComponent]) != '\0'
151 && !RTPATH_IS_SLASH(ch))
152 cchComponent++;
153
154 /*
155 * Open or create the component.
156 */
157 pszFinalPath[cchComponent] = '\0';
158 RTVFSDIR hVfsNextDir = NIL_RTVFSDIR;
159 for (uint32_t cTries = 0; cTries < 8; cTries++)
160 {
161 /* Try open it. */
162 rc = RTVfsDirOpenDir(hVfsCurDir, pszFinalPath, 0 /*fFlags*/, &hVfsNextDir);
163 if (RT_SUCCESS(rc))
164 break;
165 if ( rc != VERR_FILE_NOT_FOUND
166 && rc != VERR_PATH_NOT_FOUND)
167 {
168 if (ch == '\0')
169 RTMsgError("Failed opening directory '%s': %Rrc", pszDir, rc);
170 else
171 RTMsgError("Failed opening dir '%s' (for creating '%s'): %Rrc", pszFullFinalPath, pszDir, rc);
172 break;
173 }
174
175 /* Not found, so try create it. */
176 rc = RTVfsDirCreateDir(hVfsCurDir, pszFinalPath, pOpts->fMode, pOpts->fCreateFlags, &hVfsNextDir);
177 if (rc == VERR_ALREADY_EXISTS)
178 continue; /* We lost a creation race, try again. */
179 if (RT_SUCCESS(rc) && pOpts->fVerbose)
180 {
181 if (pszSpec)
182 RTPrintf("%s:%s\n", pszSpec, pszFullFinalPath);
183 else
184 RTPrintf("%s\n", pszFullFinalPath);
185 }
186 else if (RT_FAILURE(rc))
187 {
188 if (ch == '\0')
189 RTMsgError("Failed creating directory '%s': %Rrc", pszDir, rc);
190 else
191 RTMsgError("Failed creating dir '%s' (for '%s'): %Rrc", pszFullFinalPath, pszDir, rc);
192 }
193 break;
194 }
195 pszFinalPath[cchComponent] = ch;
196
197 RTVfsDirRelease(hVfsCurDir);
198 hVfsCurDir = hVfsNextDir;
199 pszFinalPath += cchComponent;
200 }
201 }
202 else
203 RTVfsChainMsgError("RTVfsChainOpenParentDir", pszCopy, rc, offError, NULL);
204 RTStrFree(pszCopy);
205 }
206 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
207}
208
209
210/**
211 * Create one directory.
212 *
213 * @returns exit code
214 * @param pOpts The mkdir option.
215 * @param pszDir The path to the new directory.
216 */
217static RTEXITCODE rtCmdMkDirOne(RTCMDMKDIROPTS const *pOpts, const char *pszDir)
218{
219 int rc;
220 if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszDir) )
221 rc = RTDirCreate(pszDir, pOpts->fMode, 0);
222 else
223 {
224 RTVFSDIR hVfsDir;
225 const char *pszChild;
226 uint32_t offError;
227 RTERRINFOSTATIC ErrInfo;
228 rc = RTVfsChainOpenParentDir(pszDir, 0 /*fOpen*/, &hVfsDir, &pszChild, &offError, RTErrInfoInitStatic(&ErrInfo));
229 if (RT_SUCCESS(rc))
230 {
231 rc = RTVfsDirCreateDir(hVfsDir, pszChild, pOpts->fMode, 0 /*fFlags*/, NULL);
232 RTVfsDirRelease(hVfsDir);
233 }
234 else
235 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenParentDir", pszDir, rc, offError, &ErrInfo.Core);
236 }
237 if (RT_SUCCESS(rc))
238 {
239 if (pOpts->fVerbose)
240 RTPrintf("%s\n", pszDir);
241 return RTEXITCODE_SUCCESS;
242 }
243 return RTMsgErrorExitFailure("Failed to create '%s': %Rrc", pszDir, rc);
244}
245
246
247static RTEXITCODE RTCmdMkDir(unsigned cArgs, char **papszArgs)
248{
249 /*
250 * Parse the command line.
251 */
252 static const RTGETOPTDEF s_aOptions[] =
253 {
254 /* operations */
255 { "--mode", 'm', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
256 { "--parents", 'p', RTGETOPT_REQ_NOTHING },
257 { "--always-use-vfs-chain-api", 'A', RTGETOPT_REQ_NOTHING },
258 { "--allow-content-indexing", 'i', RTGETOPT_REQ_NOTHING },
259 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
260 };
261
262 RTGETOPTSTATE GetState;
263 int rc = RTGetOptInit(&GetState, cArgs, papszArgs, s_aOptions, RT_ELEMENTS(s_aOptions), 1,
264 RTGETOPTINIT_FLAGS_OPTS_FIRST);
265 if (RT_FAILURE(rc))
266 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOpt failed: %Rrc", rc);
267
268 RTCMDMKDIROPTS Opts;
269 Opts.fVerbose = false;
270 Opts.fParents = false;
271 Opts.fAlwaysUseChainApi = false;
272 Opts.fCreateFlags = RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_NOT_CRITICAL | RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET;
273 Opts.fMode = 0775 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
274
275 RTGETOPTUNION ValueUnion;
276 while ( (rc = RTGetOpt(&GetState, &ValueUnion)) != 0
277 && rc != VINF_GETOPT_NOT_OPTION)
278 {
279 switch (rc)
280 {
281 case 'm':
282 /** @todo DOS+NT attributes and symbolic notation. */
283 Opts.fMode &= ~07777;
284 Opts.fMode |= ValueUnion.u32 & 07777;
285 break;
286
287 case 'p':
288 Opts.fParents = true;
289 break;
290
291 case 'v':
292 Opts.fVerbose = true;
293 break;
294
295 case 'A':
296 Opts.fAlwaysUseChainApi = true;
297 break;
298
299 case 'i':
300 Opts.fCreateFlags &= ~RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_SET;
301 Opts.fCreateFlags |= RTDIRCREATE_FLAGS_NOT_CONTENT_INDEXED_DONT_SET;
302 break;
303
304 case 'h':
305 RTPrintf("Usage: %s [options] <dir> [..]\n"
306 "\n"
307 "Options:\n"
308 " -m <mode>, --mode <mode>\n"
309 " The creation mode. Default is 0775.\n"
310 " -p, --parent\n"
311 " Create parent directories too. Ignore any existing directories.\n"
312 " -v, --verbose\n"
313 " Tell which directories get created.\n"
314 " -A, --always-use-vfs-chain-api\n"
315 " Always use the VFS API.\n"
316 " -i, --allow-content-indexing\n"
317 " Don't set flags to disable context indexing on windows.\n"
318 , papszArgs[0]);
319 return RTEXITCODE_SUCCESS;
320
321 case 'V':
322 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
323 return RTEXITCODE_SUCCESS;
324
325 default:
326 return RTGetOptPrintError(rc, &ValueUnion);
327 }
328 }
329
330
331 /*
332 * No files means error.
333 */
334 if (rc != VINF_GETOPT_NOT_OPTION)
335 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No directories specified.\n");
336
337 /*
338 * Work thru the specified dirs.
339 */
340 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
341 while (rc == VINF_GETOPT_NOT_OPTION)
342 {
343 if (Opts.fParents)
344 rc = rtCmdMkDirOneWithParents(&Opts, ValueUnion.psz);
345 else
346 rc = rtCmdMkDirOne(&Opts, ValueUnion.psz);
347 if (RT_FAILURE(rc))
348 rcExit = RTEXITCODE_FAILURE;
349
350 /* next */
351 rc = RTGetOpt(&GetState, &ValueUnion);
352 }
353 if (rc != 0)
354 rcExit = RTGetOptPrintError(rc, &ValueUnion);
355
356 return rcExit;
357}
358
359
360int main(int argc, char **argv)
361{
362 int rc = RTR3InitExe(argc, &argv, 0);
363 if (RT_FAILURE(rc))
364 return RTMsgInitFailure(rc);
365 return RTCmdMkDir(argc, argv);
366}
367
Note: See TracBrowser for help on using the repository browser.

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