VirtualBox

source: vbox/trunk/src/VBox/Runtime/tools/RTCp.cpp@ 73978

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

IPRT: Tweaked ntfsvfs.cpp and RTCp.cpp so it's patch files, provided the size doesn't change and they're large enough to not live in the MFT. This is very crude, but it's enormously helpful for tweaking the kernel.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: RTCp.cpp 72273 2018-05-21 12:51:27Z vboxsync $ */
2/** @file
3 * IPRT - cp like utility.
4 */
5
6/*
7 * Copyright (C) 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/vfs.h>
32
33#include <iprt/buildconfig.h>
34#include <iprt/file.h>
35#include <iprt/fs.h>
36#include <iprt/getopt.h>
37#include <iprt/initterm.h>
38#include <iprt/message.h>
39#include <iprt/mem.h>
40#include <iprt/path.h>
41#include <iprt/stream.h>
42#include <iprt/string.h>
43
44
45/*********************************************************************************************************************************
46* Structures and Typedefs *
47*********************************************************************************************************************************/
48/**
49 * CAT command options.
50 */
51typedef struct RTCMDCPOPTS
52{
53 /** -v, --verbose. */
54 bool fVerbose;
55
56 /** -H */
57 bool fFollowCommandLineSymlinks;
58
59 /** Set if recursive copy. */
60 bool fRecursive;
61 /** -x, --one-filesystem. */
62 bool fOneFileSystem;
63
64 /** Special --no-replace-nor-trucate hack for basic NTFS write support. */
65 bool fNoReplaceNorTruncate;
66
67 /** Number of sources. */
68 size_t cSources;
69 /** Source files/dirs. */
70 const char **papszSources;
71 /** Destination dir/file. */
72 const char *pszDestination;
73} RTCMDCPOPTS;
74/** Pointer to const CAT options. */
75typedef RTCMDCPOPTS const *PCRTCMDCPOPTS;
76
77
78
79/**
80 * Does the copying, source by source.
81 *
82 * @returns exit code.
83 * @param pOpts Options.
84 */
85static RTEXITCODE rtCmdCpDoIt(PCRTCMDCPOPTS pOpts)
86{
87 /*
88 * Check out what the destination is.
89 */
90/** @todo need to cache + share VFS chain elements here! */
91 RTERRINFOSTATIC ErrInfo;
92 uint32_t offError;
93 RTFSOBJINFO DstObjInfo;
94 int rc = RTVfsChainQueryInfo(pOpts->pszDestination, &DstObjInfo, RTFSOBJATTRADD_UNIX, RTPATH_F_FOLLOW_LINK,
95 &offError, RTErrInfoInitStatic(&ErrInfo));
96 if (RT_SUCCESS(rc))
97 {
98 if (pOpts->cSources > 1 && !RTFS_IS_DIRECTORY(DstObjInfo.Attr.fMode))
99 return RTMsgErrorExitFailure("Mutiple files to copy and destination is not a directory!");
100 }
101 else if (rc != VERR_FILE_NOT_FOUND)
102 return RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pOpts->pszDestination, rc, offError, &ErrInfo.Core);
103 else
104 RT_ZERO(DstObjInfo);
105 AssertCompile(!RTFS_IS_DIRECTORY(0));
106
107 /*
108 * Process the sources.
109 */
110 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
111 for (size_t iSrc = 0; iSrc < pOpts->cSources; iSrc++)
112 {
113 const char *pszSrc = pOpts->papszSources[iSrc];
114 RTFSOBJINFO SrcObjInfo;
115 RT_ZERO(SrcObjInfo);
116 rc = RTVfsChainQueryInfo(pszSrc, &SrcObjInfo, RTFSOBJATTRADD_UNIX,
117 pOpts->fFollowCommandLineSymlinks ? RTPATH_F_FOLLOW_LINK : RTPATH_F_ON_LINK,
118 &offError, RTErrInfoInitStatic(&ErrInfo));
119 if (RT_FAILURE(rc))
120 {
121 rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainQueryInfo", pszSrc, rc, offError, &ErrInfo.Core);
122 continue;
123 }
124
125 /*
126 * Regular file.
127 */
128 if (RTFS_IS_FILE(SrcObjInfo.Attr.fMode))
129 {
130 /* Open source. */
131 RTVFSFILE hVfsSrc;
132 rc = RTVfsChainOpenFile(pszSrc, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE,
133 &hVfsSrc, &offError, RTErrInfoInitStatic(&ErrInfo));
134 if (RT_SUCCESS(rc))
135 {
136 /* Make destination name if necessary and open destination.
137 Note! RTFILE_O_READ needed for VFS chains. */
138 char szDstPath[RTPATH_MAX];
139 const char *pszDst = pOpts->pszDestination;
140 if (RTFS_IS_DIRECTORY(DstObjInfo.Attr.fMode))
141 {
142 rc = RTPathJoin(szDstPath, sizeof(szDstPath), pszDst, RTPathFilename(pszSrc));
143 pszDst = szDstPath;
144 }
145 if (RT_SUCCESS(rc))
146 {
147 RTVFSFILE hVfsDst;
148 uint64_t fDstFlags = (pOpts->fNoReplaceNorTruncate ? RTFILE_O_OPEN_CREATE : RTFILE_O_CREATE_REPLACE)
149 | RTFILE_O_READWRITE | RTFILE_O_DENY_WRITE | (0666 << RTFILE_O_CREATE_MODE_SHIFT);
150 rc = RTVfsChainOpenFile(pszDst, fDstFlags, &hVfsDst, &offError, RTErrInfoInitStatic(&ErrInfo));
151 if (RT_SUCCESS(rc))
152 {
153 /* Copy the bytes. */
154 RTVFSIOSTREAM hVfsIosSrc = RTVfsFileToIoStream(hVfsSrc);
155 RTVFSIOSTREAM hVfsIosDst = RTVfsFileToIoStream(hVfsDst);
156
157 rc = RTVfsUtilPumpIoStreams(hVfsIosSrc, hVfsIosDst, 0);
158
159 if (RT_SUCCESS(rc))
160 {
161 if (pOpts->fVerbose)
162 RTPrintf("'%s' -> '%s'\n", pszSrc, pszDst);
163 }
164 else
165 rcExit = RTMsgErrorExitFailure("RTVfsUtilPumpIoStreams failed for '%s' -> '%s': %Rrc",
166 pszSrc, pszDst, rc);
167 RTVfsIoStrmRelease(hVfsIosSrc);
168 RTVfsIoStrmRelease(hVfsIosDst);
169 RTVfsFileRelease(hVfsDst);
170 }
171 else
172 rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainOpenFile", pszDst, rc, offError, &ErrInfo.Core);
173 }
174 else
175 rcExit = RTMsgErrorExitFailure("Destination path too long for source #%u (%Rrc): %s", iSrc, pszSrc, rc);
176 RTVfsFileRelease(hVfsSrc);
177 }
178 else
179 rcExit = RTVfsChainMsgErrorExitFailure("RTVfsChainOpenFile", pszSrc, rc, offError, &ErrInfo.Core);
180 }
181 /*
182 * Copying a directory requires the -R option to be active.
183 */
184 else if (RTFS_IS_DIRECTORY(SrcObjInfo.Attr.fMode))
185 {
186 if (pOpts->fRecursive)
187 {
188 /** @todo recursive copy */
189 rcExit = RTMsgErrorExitFailure("Recursion not implemented yet!");
190 }
191 else
192 rcExit = RTMsgErrorExitFailure("Source #%u is a directory: %s", iSrc + 1, pszSrc);
193 }
194 /*
195 * We currently don't support copying any other file types.
196 */
197 else
198 rcExit = RTMsgErrorExitFailure("Source #%u neither a file nor a directory: %s", iSrc + 1, pszSrc);
199 }
200 return rcExit;
201}
202
203
204/**
205 * A /bin/cp clone.
206 *
207 * @returns Program exit code.
208 *
209 * @param cArgs The number of arguments.
210 * @param papszArgs The argument vector. (Note that this may be
211 * reordered, so the memory must be writable.)
212 */
213RTEXITCODE RTCmdCp(unsigned cArgs, char **papszArgs)
214{
215
216 /*
217 * Parse the command line.
218 */
219 static const RTGETOPTDEF s_aOptions[] =
220 {
221 { "--archive", 'a', RTGETOPT_REQ_NOTHING },
222 { "--backup", 'B', RTGETOPT_REQ_STRING },
223 { "", 'b', RTGETOPT_REQ_NOTHING },
224 { "--copy-contents", 1024, RTGETOPT_REQ_NOTHING },
225 { "", 'd', RTGETOPT_REQ_NOTHING },
226 { "--no-dereference", 'P', RTGETOPT_REQ_NOTHING },
227 { "--force", 'f', RTGETOPT_REQ_NOTHING },
228 { "", 'H', RTGETOPT_REQ_NOTHING },
229 { "--link", 'l', RTGETOPT_REQ_NOTHING },
230 { "--dereference", 'L', RTGETOPT_REQ_NOTHING },
231 { "", 'p', RTGETOPT_REQ_NOTHING },
232 { "--preserve", 1026, RTGETOPT_REQ_STRING },
233 { "--no-preserve", 1027, RTGETOPT_REQ_STRING },
234 { "--recursive", 'R', RTGETOPT_REQ_NOTHING },
235 { "--remove-destination", 1028, RTGETOPT_REQ_NOTHING },
236 { "--reply", 1029, RTGETOPT_REQ_STRING },
237 { "--sparse", 1030, RTGETOPT_REQ_STRING },
238 { "--strip-trailing-slashes", 1031, RTGETOPT_REQ_NOTHING },
239 { "--symbolic-links", 's', RTGETOPT_REQ_NOTHING },
240 { "--suffix", 'S', RTGETOPT_REQ_STRING },
241 { "--target-directory", 't', RTGETOPT_REQ_STRING },
242 { "--no-target-directory", 'T', RTGETOPT_REQ_NOTHING },
243 { "--update", 'u', RTGETOPT_REQ_NOTHING },
244 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
245 { "--one-file-system", 'x', RTGETOPT_REQ_NOTHING },
246 { "--no-replace-nor-trucate", 1032, RTGETOPT_REQ_NOTHING },
247 };
248
249 RTCMDCPOPTS Opts;
250 Opts.fVerbose = false;
251 Opts.fFollowCommandLineSymlinks = false;
252 Opts.fRecursive = false;
253 Opts.fOneFileSystem = false;
254 Opts.fNoReplaceNorTruncate = false;
255 Opts.pszDestination = NULL;
256 Opts.cSources = 0;
257 Opts.papszSources = (const char **)RTMemAllocZ(sizeof(const char *) * (cArgs + 2));
258 AssertReturn(Opts.papszSources, RTEXITCODE_FAILURE);
259
260 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
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_SUCCESS(rc))
266 {
267 bool fContinue = true;
268 do
269 {
270 RTGETOPTUNION ValueUnion;
271 int chOpt = RTGetOpt(&GetState, &ValueUnion);
272 switch (chOpt)
273 {
274 case 0:
275 if (Opts.pszDestination == NULL && Opts.cSources == 0)
276 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing source and destination");
277 else if (Opts.pszDestination == NULL && Opts.cSources == 1)
278 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing destination");
279 else if (Opts.pszDestination != NULL && Opts.cSources == 0)
280 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing source");
281 else
282 {
283 if (Opts.pszDestination == NULL && Opts.cSources > 0)
284 Opts.pszDestination = Opts.papszSources[--Opts.cSources];
285 Assert(Opts.cSources > 0);
286 rcExit = rtCmdCpDoIt(&Opts);
287 }
288 fContinue = false;
289 break;
290
291 case VINF_GETOPT_NOT_OPTION:
292 Assert(Opts.cSources < cArgs);
293 Opts.papszSources[Opts.cSources++] = ValueUnion.psz;
294 break;
295
296 case 'H':
297 Opts.fFollowCommandLineSymlinks = true;
298 break;
299
300 case 'R':
301 Opts.fRecursive = true;
302 break;
303 case 'x':
304 Opts.fOneFileSystem = true;
305 break;
306
307 case 'v':
308 Opts.fVerbose = true;
309 break;
310
311 case 1032:
312 Opts.fNoReplaceNorTruncate = true;
313 break;
314
315 case 'h':
316 RTPrintf("Usage: to be written\nOption dump:\n");
317 for (unsigned i = 0; i < RT_ELEMENTS(s_aOptions); i++)
318 RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
319 fContinue = false;
320 break;
321
322 case 'V':
323 RTPrintf("%sr%d\n", RTBldCfgVersion(), RTBldCfgRevision());
324 fContinue = false;
325 break;
326
327 default:
328 rcExit = RTGetOptPrintError(chOpt, &ValueUnion);
329 fContinue = false;
330 break;
331 }
332 } while (fContinue);
333 }
334 else
335 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTGetOptInit: %Rrc", rc);
336 RTMemFree(Opts.papszSources);
337 return rcExit;
338}
339
340
341int main(int argc, char **argv)
342{
343 int rc = RTR3InitExe(argc, &argv, 0);
344 if (RT_FAILURE(rc))
345 return RTMsgInitFailure(rc);
346 return RTCmdCp(argc, argv);
347}
348
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