VirtualBox

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

Last change on this file since 100240 was 99775, checked in by vboxsync, 19 months ago

*: Mark functions as static if not used outside of a given compilation unit. Enables the compiler to optimize inlining, reduces the symbol tables, exposes unused functions and in some rare cases exposes mismtaches between function declarations and definitions, but most importantly reduces the number of parfait reports for the extern-function-no-forward-declaration category. This should not result in any functional changes, bugref:3409

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