VirtualBox

source: kBuild/trunk/src/kmk/kmkbuiltin/mv.c@ 3192

Last change on this file since 3192 was 3192, checked in by bird, 7 years ago

kmkbuiltin: funnel output thru output.c (usually via err.c).

  • Property svn:eol-style set to native
File size: 14.8 KB
Line 
1/*-
2 * Copyright (c) 1989, 1993, 1994
3 * The Regents of the University of California. All rights reserved.
4 *
5 * This code is derived from software contributed to Berkeley by
6 * Ken Smith of The State University of New York at Buffalo.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#if 0
34#ifndef lint
35static char const copyright[] =
36"@(#) Copyright (c) 1989, 1993, 1994\n\
37 The Regents of the University of California. All rights reserved.\n";
38#endif /* not lint */
39
40#ifndef lint
41static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94";
42#endif /* not lint */
43#endif
44#if 0
45#include <sys/cdefs.h>
46__FBSDID("$FreeBSD: src/bin/mv/mv.c,v 1.46 2005/09/05 04:36:08 csjp Exp $");
47#endif
48
49
50/*********************************************************************************************************************************
51* Header Files *
52*********************************************************************************************************************************/
53#include "config.h"
54#include <sys/types.h>
55#ifndef _MSC_VER
56# ifdef CROSS_DEVICE_MOVE
57# include <sys/acl.h>
58# endif
59# include <sys/param.h>
60# include <sys/time.h>
61# include <sys/wait.h>
62# if !defined(__HAIKU__) && !defined(__gnu_hurd__)
63# include <sys/mount.h>
64# endif
65#endif
66#include <sys/stat.h>
67
68#include "err.h"
69#include <errno.h>
70#include <fcntl.h>
71#include <grp.h>
72#include <limits.h>
73#include <paths.h>
74#include <pwd.h>
75#include <stdio.h>
76#include <stdlib.h>
77#include <string.h>
78#ifndef __HAIKU__
79# include <sysexits.h>
80#endif
81#include <unistd.h>
82#include "getopt.h"
83#ifdef __sun__
84# include "solfakes.h"
85#endif
86#ifdef __HAIKU__
87# include "haikufakes.h"
88#endif
89#ifdef _MSC_VER
90# include "mscfakes.h"
91#endif
92#include "kmkbuiltin.h"
93
94
95/*********************************************************************************************************************************
96* Structures and Typedefs *
97*********************************************************************************************************************************/
98typedef struct MVINSTANCE
99{
100 PKMKBUILTINCTX pCtx;
101 int fflg, iflg, nflg, vflg;
102} MVINSTANCE;
103typedef MVINSTANCE *PMVINSTANCE;
104
105
106/*********************************************************************************************************************************
107* Global Variables *
108*********************************************************************************************************************************/
109static struct option long_options[] =
110{
111 { "help", no_argument, 0, 261 },
112 { "version", no_argument, 0, 262 },
113 { 0, 0, 0, 0 },
114};
115
116
117/*********************************************************************************************************************************
118* Internal Functions *
119*********************************************************************************************************************************/
120extern void bsd_strmode(mode_t mode, char *p); /* strmode.c */
121
122static int do_move(PMVINSTANCE, char *, char *);
123#ifdef CROSS_DEVICE_MOVE
124static int fastcopy(char *, char *, struct stat *);
125static int copy(char *, char *);
126#endif
127static int usage(PKMKBUILTINCTX, int);
128
129
130#if !defined(__FreeBSD__) && !defined(__APPLE__) && !defined(__DragonFly__) && !defined(__OpenBSD__)
131# ifdef __OS2__
132static
133# endif
134const char *user_from_uid(uid_t id, int x)
135{
136 static char s_buf[64];
137 sprintf(s_buf, "%ld", (long int)id);
138 (void)x;
139 return s_buf;
140}
141# ifdef __OS2__
142static
143# endif
144const char *group_from_gid(gid_t id, int x)
145{
146 static char s_buf[64];
147 sprintf(s_buf, "%ld", (long int)id);
148 (void)x;
149 return s_buf;
150}
151#endif /* 'not in libc' */
152
153
154int
155kmk_builtin_mv(int argc, char **argv, char **envp, PKMKBUILTINCTX pCtx)
156{
157 MVINSTANCE This;
158 size_t baselen, len;
159 int rval;
160 char *p, *endp;
161 struct stat sb;
162 int ch;
163 char path[PATH_MAX];
164
165 /* Initialize instance. */
166 This.pCtx = pCtx;
167 This.fflg = 0;
168 This.iflg = 0;
169 This.nflg = 0;
170 This.vflg = 0;
171
172 /* kmk: reset getopt and set progname */
173 opterr = 1;
174 optarg = NULL;
175 optopt = 0;
176 optind = 0; /* init */
177
178 while ((ch = getopt_long(argc, argv, "finv", long_options, NULL)) != -1)
179 switch (ch) {
180 case 'i':
181 This.iflg = 1;
182 This.fflg = This.nflg = 0;
183 break;
184 case 'f':
185 This.fflg = 1;
186 This.iflg = This.nflg = 0;
187 break;
188 case 'n':
189 This.nflg = 1;
190 This.fflg = This.iflg = 0;
191 break;
192 case 'v':
193 This.vflg = 1;
194 break;
195 case 261:
196 usage(pCtx, 0);
197 return 0;
198 case 262:
199 return kbuild_version(argv[0]);
200 default:
201 return usage(pCtx, 1);
202 }
203 argc -= optind;
204 argv += optind;
205
206 if (argc < 2)
207 return usage(pCtx, 1);
208
209 /*
210 * If the stat on the target fails or the target isn't a directory,
211 * try the move. More than 2 arguments is an error in this case.
212 */
213 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
214 if (argc > 2)
215 return usage(pCtx, 1);
216 return do_move(&This, argv[0], argv[1]);
217 }
218
219 /* It's a directory, move each file into it. */
220 baselen = strlen(argv[argc - 1]);
221 if (baselen > sizeof(path) - 1)
222 return errx(pCtx, 1, "%s: destination pathname too long", *argv);
223 memcpy(path, argv[argc - 1], baselen);
224 endp = &path[baselen];
225 *endp = '\0';
226#if defined(_MSC_VER) || defined(__EMX__)
227 if (!baselen || (*(endp - 1) != '/' && *(endp - 1) != '\\' && *(endp - 1) != ':')) {
228#else
229 if (!baselen || *(endp - 1) != '/') {
230#endif
231 *endp++ = '/';
232 ++baselen;
233 }
234 for (rval = 0; --argc; ++argv) {
235 /*
236 * Find the last component of the source pathname. It
237 * may have trailing slashes.
238 */
239 p = *argv + strlen(*argv);
240#if defined(_MSC_VER) || defined(__EMX__)
241 while (p != *argv && (p[-1] == '/' || p[-1] == '\\'))
242 --p;
243 while (p != *argv && p[-1] != '/' && p[-1] != '/' && p[-1] != ':')
244 --p;
245#else
246 while (p != *argv && p[-1] == '/')
247 --p;
248 while (p != *argv && p[-1] != '/')
249 --p;
250#endif
251
252 if ((baselen + (len = strlen(p))) >= PATH_MAX) {
253 warnx(pCtx, "%s: destination pathname too long", *argv);
254 rval = 1;
255 } else {
256 memmove(endp, p, (size_t)len + 1);
257 if (do_move(&This, *argv, path))
258 rval = 1;
259 }
260 }
261 return rval;
262}
263
264#ifdef KMK_BUILTIN_STANDALONE
265int main(int argc, char **argv, char **envp)
266{
267 KMKBUILTINCTX Ctx = { "kmk_mv", NULL };
268 return kmk_builtin_mv(argc, argv, envp, &Ctx);
269}
270#endif
271
272static int
273do_move(PMVINSTANCE pThis, char *from, char *to)
274{
275 struct stat sb;
276 int ask, ch, first;
277 char modep[15];
278
279 /*
280 * Check access. If interactive and file exists, ask user if it
281 * should be replaced. Otherwise if file exists but isn't writable
282 * make sure the user wants to clobber it.
283 */
284 if (!pThis->fflg && !access(to, F_OK)) {
285
286 /* prompt only if source exist */
287 if (lstat(from, &sb) == -1) {
288 warn(pThis->pCtx, "%s", from);
289 return (1);
290 }
291
292#define YESNO "(y/n [n]) "
293 ask = 0;
294 if (pThis->nflg) {
295 if (pThis->vflg)
296 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s not overwritten\n", to);
297 return (0);
298 } else if (pThis->iflg) {
299 (void)fprintf(stderr, "overwrite %s? %s", to, YESNO);
300 ask = 1;
301 } else if (access(to, W_OK) && !stat(to, &sb)) {
302 bsd_strmode(sb.st_mode, modep);
303 (void)fprintf(stderr, "override %s%s%s/%s for %s? %s",
304 modep + 1, modep[9] == ' ' ? "" : " ",
305 user_from_uid((unsigned long)sb.st_uid, 0),
306 group_from_gid((unsigned long)sb.st_gid, 0), to, YESNO);
307 ask = 1;
308 }
309 if (ask) {
310 fflush(stderr);
311 first = ch = getchar();
312 while (ch != '\n' && ch != EOF)
313 ch = getchar();
314 if (first != 'y' && first != 'Y') {
315 kmk_builtin_ctx_printf(pThis->pCtx, 1, "not overwritten\n");
316 return (0);
317 }
318 }
319 }
320 if (!rename(from, to)) {
321 if (pThis->vflg)
322 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s -> %s\n", from, to);
323 return (0);
324 }
325#ifdef _MSC_VER
326 if (errno == EEXIST) {
327 remove(to);
328 if (!rename(from, to)) {
329 if (pThis->vflg)
330 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s -> %s\n", from, to);
331 return (0);
332 }
333 }
334#endif
335
336 if (errno == EXDEV) {
337#ifndef CROSS_DEVICE_MOVE
338 warnx(pThis->pCtx, "cannot move `%s' to a different device: `%s'", from, to);
339 return (1);
340#else
341 struct statfs sfs;
342 char path[PATH_MAX];
343
344 /*
345 * If the source is a symbolic link and is on another
346 * filesystem, it can be recreated at the destination.
347 */
348 if (lstat(from, &sb) == -1) {
349 warn(pThis->pCtx, "%s", from);
350 return (1);
351 }
352 if (!S_ISLNK(sb.st_mode)) {
353 /* Can't mv(1) a mount point. */
354 if (realpath(from, path) == NULL) {
355 warnx(pThis->pCtx, "cannot resolve %s: %s", from, path);
356 return (1);
357 }
358 if (!statfs(path, &sfs) &&
359 !strcmp(path, sfs.f_mntonname)) {
360 warnx(pThis->pCtx, "cannot rename a mount point");
361 return (1);
362 }
363 }
364#endif
365 } else {
366 warn(pThis->pCtx, "rename %s to %s", from, to);
367 return (1);
368 }
369
370#ifdef CROSS_DEVICE_MOVE
371 /*
372 * If rename fails because we're trying to cross devices, and
373 * it's a regular file, do the copy internally; otherwise, use
374 * cp and rm.
375 */
376 if (lstat(from, &sb)) {
377 warn(pThis->pCtx, "%s", from);
378 return (1);
379 }
380 return (S_ISREG(sb.st_mode) ?
381 fastcopy(pThis, from, to, &sb) : copy(pThis, from, to));
382#endif
383}
384
385#ifdef CROSS_DEVICE_MOVE
386int
387static fastcopy(char *from, char *to, struct stat *sbp)
388{
389 struct timeval tval[2];
390 static u_int blen;
391 static char *bp;
392 mode_t oldmode;
393 int nread, from_fd, to_fd;
394 acl_t acl;
395
396 if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
397 warn("%s", from);
398 return (1);
399 }
400 if (blen < sbp->st_blksize) {
401 if (bp != NULL)
402 free(bp);
403 if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) {
404 blen = 0;
405 warnx("malloc failed");
406 return (1);
407 }
408 blen = sbp->st_blksize;
409 }
410 while ((to_fd =
411 open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) {
412 if (errno == EEXIST && unlink(to) == 0)
413 continue;
414 warn("%s", to);
415 (void)close(from_fd);
416 return (1);
417 }
418 while ((nread = read(from_fd, bp, (size_t)blen)) > 0)
419 if (write(to_fd, bp, (size_t)nread) != nread) {
420 warn("%s", to);
421 goto err;
422 }
423 if (nread < 0) {
424 warn("%s", from);
425err: if (unlink(to))
426 warn("%s: remove", to);
427 (void)close(from_fd);
428 (void)close(to_fd);
429 return (1);
430 }
431
432 oldmode = sbp->st_mode & ALLPERMS;
433 if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) {
434 warn("%s: set owner/group (was: %lu/%lu)", to,
435 (u_long)sbp->st_uid, (u_long)sbp->st_gid);
436 if (oldmode & (S_ISUID | S_ISGID)) {
437 warnx(
438"%s: owner/group changed; clearing suid/sgid (mode was 0%03o)",
439 to, oldmode);
440 sbp->st_mode &= ~(S_ISUID | S_ISGID);
441 }
442 }
443 /*
444 * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect
445 * for dest_file, then it's ACLs shall reflect the ACLs of the
446 * source_file.
447 */
448 if (fpathconf(to_fd, _PC_ACL_EXTENDED) == 1 &&
449 fpathconf(from_fd, _PC_ACL_EXTENDED) == 1) {
450 acl = acl_get_fd(from_fd);
451 if (acl == NULL)
452 warn("failed to get acl entries while setting %s",
453 from);
454 else if (acl_set_fd(to_fd, acl) < 0)
455 warn("failed to set acl entries for %s", to);
456 }
457 (void)close(from_fd);
458 if (fchmod(to_fd, sbp->st_mode))
459 warn("%s: set mode (was: 0%03o)", to, oldmode);
460 /*
461 * XXX
462 * NFS doesn't support chflags; ignore errors unless there's reason
463 * to believe we're losing bits. (Note, this still won't be right
464 * if the server supports flags and we were trying to *remove* flags
465 * on a file that we copied, i.e., that we didn't create.)
466 */
467 errno = 0;
468 if (fchflags(to_fd, (u_long)sbp->st_flags))
469 if (errno != EOPNOTSUPP || sbp->st_flags != 0)
470 warn("%s: set flags (was: 0%07o)", to, sbp->st_flags);
471
472 tval[0].tv_sec = sbp->st_atime;
473 tval[1].tv_sec = sbp->st_mtime;
474 tval[0].tv_usec = tval[1].tv_usec = 0;
475 if (utimes(to, tval))
476 warn("%s: set times", to);
477
478 if (close(to_fd)) {
479 warn("%s", to);
480 return (1);
481 }
482
483 if (unlink(from)) {
484 warn("%s: remove", from);
485 return (1);
486 }
487 if (vflg)
488 kmk_builtin_ctx_printf(pThis->pCtx, 0, "%s -> %s\n", from, to);
489 return (0);
490}
491
492int
493copy(char *from, char *to)
494{
495 int pid, status;
496
497 if ((pid = fork()) == 0) {
498 execl(_PATH_CP, "mv", vflg ? "-PRpv" : "-PRp", "--", from, to,
499 (char *)NULL);
500 warn("%s", _PATH_CP);
501 _exit(1);
502 }
503 if (waitpid(pid, &status, 0) == -1) {
504 warn("%s: waitpid", _PATH_CP);
505 return (1);
506 }
507 if (!WIFEXITED(status)) {
508 warnx("%s: did not terminate normally", _PATH_CP);
509 return (1);
510 }
511 if (WEXITSTATUS(status)) {
512 warnx("%s: terminated with %d (non-zero) status",
513 _PATH_CP, WEXITSTATUS(status));
514 return (1);
515 }
516 if (!(pid = vfork())) {
517 execl(_PATH_RM, "mv", "-rf", "--", from, (char *)NULL);
518 warn("%s", _PATH_RM);
519 _exit(1);
520 }
521 if (waitpid(pid, &status, 0) == -1) {
522 warn("%s: waitpid", _PATH_RM);
523 return (1);
524 }
525 if (!WIFEXITED(status)) {
526 warnx("%s: did not terminate normally", _PATH_RM);
527 return (1);
528 }
529 if (WEXITSTATUS(status)) {
530 warnx("%s: terminated with %d (non-zero) status",
531 _PATH_RM, WEXITSTATUS(status));
532 return (1);
533 }
534 return (0);
535}
536#endif /* CROSS_DEVICE_MOVE */
537
538
539static int
540usage(PKMKBUILTINCTX pCtx, int fIsErr)
541{
542 kmk_builtin_ctx_printf(pCtx, fIsErr,
543 "usage: %s [-f | -i | -n] [-v] source target\n"
544 " or: %s [-f | -i | -n] [-v] source ... directory\n"
545 " or: %s --help\n"
546 " or: %s --version\n",
547 pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName, pCtx->pszProgName);
548 return EX_USAGE;
549}
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