VirtualBox

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

Last change on this file since 69716 was 69716, checked in by vboxsync, 7 years ago

IPRT: More work on directory relative APIs (NT mainly) and VFS; introducing RTMkDir (test) tool.

  • Added RTVfsDirCreateDir
  • Added RTVfsChainOpenParentDir and RTVfsChainSplitOffFinalPath.
  • Added new tool for testing this called RTMkDir.
  • Fixed directory traversal problem with stddir by making it okay to return VERR_FILE_NOT_FOUND as well.
  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 12.2 KB
Line 
1/* $Id: RTMkDir.cpp 69716 2017-11-16 14:31:25Z 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 /** The directory mode. */
55 RTFMODE fMode;
56 /** The mode to apply to parent directories we create. */
57 RTFMODE fParentMode;
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, ch == '\0' ? pOpts->fMode : pOpts->fParentMode,
177 0 /*fFlags*/, &hVfsNextDir);
178 if (rc == VERR_ALREADY_EXISTS)
179 continue; /* We lost a creation race, try again. */
180 if (RT_SUCCESS(rc) && pOpts->fVerbose)
181 {
182 if (pszSpec)
183 RTPrintf("%s:%s\n", pszSpec, pszFullFinalPath);
184 else
185 RTPrintf("%s\n", pszFullFinalPath);
186 }
187 else if (RT_FAILURE(rc))
188 {
189 if (ch == '\0')
190 RTMsgError("Failed creating directory '%s': %Rrc", pszDir, rc);
191 else
192 RTMsgError("Failed creating dir '%s' (for '%s'): %Rrc", pszFullFinalPath, pszDir, rc);
193 }
194 break;
195 }
196 pszFinalPath[cchComponent] = ch;
197
198 RTVfsDirRelease(hVfsCurDir);
199 hVfsCurDir = hVfsNextDir;
200 pszFinalPath += cchComponent;
201 }
202 }
203 else
204 RTVfsChainMsgError("RTVfsChainOpenParentDir", pszCopy, rc, offError, NULL);
205 RTStrFree(pszCopy);
206 }
207 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
208}
209
210
211/**
212 * Create one directory.
213 *
214 * @returns exit code
215 * @param pOpts The mkdir option.
216 * @param pszDir The path to the new directory.
217 */
218static RTEXITCODE rtCmdMkDirOne(RTCMDMKDIROPTS const *pOpts, const char *pszDir)
219{
220 int rc;
221 if (!pOpts->fAlwaysUseChainApi && !RTVfsChainIsSpec(pszDir) )
222 rc = RTDirCreate(pszDir, pOpts->fMode, 0);
223 else
224 {
225 RTVFSDIR hVfsDir;
226 const char *pszChild;
227 uint32_t offError;
228 RTERRINFOSTATIC ErrInfo;
229 rc = RTVfsChainOpenParentDir(pszDir, 0 /*fOpen*/, &hVfsDir, &pszChild, &offError, RTErrInfoInitStatic(&ErrInfo));
230 if (RT_SUCCESS(rc))
231 {
232 rc = RTVfsDirCreateDir(hVfsDir, pszChild, pOpts->fMode, 0 /*fFlags*/, NULL);
233 RTVfsDirRelease(hVfsDir);
234 }
235 else
236 return RTVfsChainMsgErrorExitFailure("RTVfsChainOpenParentDir", pszDir, rc, offError, &ErrInfo.Core);
237 }
238 if (RT_SUCCESS(rc))
239 {
240 if (pOpts->fVerbose)
241 RTPrintf("%s\n", pszDir);
242 return RTEXITCODE_SUCCESS;
243 }
244 return RTMsgErrorExitFailure("Failed to create '%s': %Rrc", pszDir, rc);
245}
246
247
248static RTEXITCODE RTCmdMkDir(unsigned cArgs, char **papszArgs)
249{
250 /*
251 * Parse the command line.
252 */
253 static const RTGETOPTDEF s_aOptions[] =
254 {
255 /* operations */
256 { "--mode", 'm', RTGETOPT_REQ_UINT32 | RTGETOPT_FLAG_OCT },
257 { "--parents", 'p', RTGETOPT_REQ_NOTHING },
258 { "--always-use-vfs-chain-api", 'A', 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.fMode = 0775 | RTFS_TYPE_DIRECTORY | RTFS_DOS_DIRECTORY;
273 Opts.fParentMode = 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 Opts.fParentMode &= ~07777;
286 Opts.fParentMode |= 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 case 'h':
301 RTPrintf("Usage: %s [-pv] [-m|--mode <mode>] <dir> [..]\n", papszArgs[0]);
302 return RTEXITCODE_SUCCESS;
303
304 case 'V':
305 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
306 return RTEXITCODE_SUCCESS;
307
308 default:
309 return RTGetOptPrintError(rc, &ValueUnion);
310 }
311 }
312
313
314 /*
315 * No files means error.
316 */
317 if (rc != VINF_GETOPT_NOT_OPTION)
318 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No directories specified.\n");
319
320 /*
321 * Work thru the specified dirs.
322 */
323 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
324 while (rc == VINF_GETOPT_NOT_OPTION)
325 {
326 if (Opts.fParents)
327 rc = rtCmdMkDirOneWithParents(&Opts, ValueUnion.psz);
328 else
329 rc = rtCmdMkDirOne(&Opts, ValueUnion.psz);
330 if (RT_FAILURE(rc))
331 rcExit = RTEXITCODE_FAILURE;
332
333 /* next */
334 rc = RTGetOpt(&GetState, &ValueUnion);
335 }
336 if (rc != 0)
337 rcExit = RTGetOptPrintError(rc, &ValueUnion);
338
339 return rcExit;
340}
341
342
343int main(int argc, char **argv)
344{
345 int rc = RTR3InitExe(argc, &argv, 0);
346 if (RT_FAILURE(rc))
347 return RTMsgInitFailure(rc);
348 return RTCmdMkDir(argc, argv);
349}
350
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