VirtualBox

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

Last change on this file since 1297 was 1183, checked in by bird, 17 years ago

Added --version and --help to all builtins.

  • Property svn:eol-style set to native
File size: 12.0 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#include <sys/types.h>
50#ifndef _MSC_VER
51# ifndef __OS2__
52# include <sys/acl.h>
53# endif
54# include <sys/param.h>
55# include <sys/time.h>
56# include <sys/wait.h>
57# include <sys/mount.h>
58#endif
59#include <sys/stat.h>
60
61#include "err.h"
62#include <errno.h>
63#include <fcntl.h>
64#include <grp.h>
65#include <limits.h>
66#include <paths.h>
67#include <pwd.h>
68#include <stdio.h>
69#include <stdlib.h>
70#include <string.h>
71#include <sysexits.h>
72#include <unistd.h>
73
74#ifdef __sun__
75# include "solfakes.h"
76#endif
77#ifdef _MSC_VER
78# include "mscfakes.h"
79#endif
80
81#include "kmkbuiltin.h"
82
83
84static int fflg, iflg, nflg, vflg;
85static struct option long_options[] =
86{
87 { "help", no_argument, 0, 261 },
88 { "version", no_argument, 0, 262 },
89 { 0, 0, 0, 0 },
90};
91
92
93static int do_move(char *, char *);
94#ifdef CROSS_DEVICE_MOVE
95static int fastcopy(char *, char *, struct stat *);
96static int copy(char *, char *);
97#endif
98static int usage(FILE *);
99
100#if !defined(__FreeBSD__) && !defined(__APPLE__)
101extern void strmode(mode_t mode, char *p);
102#endif
103
104#if !defined(__FreeBSD__) && !defined(__APPLE__)
105# ifdef __OS2__
106static
107# endif
108const char *user_from_uid(uid_t id, int x)
109{
110 static char s_buf[64];
111 sprintf(s_buf, "%ld", id);
112 return s_buf;
113}
114# ifdef __OS2__
115static
116# endif
117const char *group_from_gid(gid_t id, int x)
118{
119 static char s_buf[64];
120 sprintf(s_buf, "%ld", id);
121 return s_buf;
122}
123#endif
124
125
126int
127kmk_builtin_mv(int argc, char *argv[], char **envp)
128{
129 size_t baselen, len;
130 int rval;
131 char *p, *endp;
132 struct stat sb;
133 int ch;
134 char path[PATH_MAX];
135
136 /* kmk: reinitialize globals */
137 fflg = iflg = nflg = vflg = 0;
138
139 /* kmk: reset getopt and set progname */
140 g_progname = argv[0];
141 opterr = 1;
142 optarg = NULL;
143 optopt = 0;
144 optind = 0; /* init */
145
146 while ((ch = getopt_long(argc, argv, "finv", long_options, NULL)) != -1)
147 switch (ch) {
148 case 'i':
149 iflg = 1;
150 fflg = nflg = 0;
151 break;
152 case 'f':
153 fflg = 1;
154 iflg = nflg = 0;
155 break;
156 case 'n':
157 nflg = 1;
158 fflg = iflg = 0;
159 break;
160 case 'v':
161 vflg = 1;
162 break;
163 case 261:
164 usage(stdout);
165 return 0;
166 case 262:
167 return kbuild_version(argv[0]);
168 default:
169 return usage(stderr);
170 }
171 argc -= optind;
172 argv += optind;
173
174 if (argc < 2)
175 return usage(stderr);
176
177 /*
178 * If the stat on the target fails or the target isn't a directory,
179 * try the move. More than 2 arguments is an error in this case.
180 */
181 if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) {
182 if (argc > 2)
183 return usage(stderr);
184 return do_move(argv[0], argv[1]);
185 }
186
187 /* It's a directory, move each file into it. */
188 if (strlen(argv[argc - 1]) > sizeof(path) - 1)
189 return errx(1, "%s: destination pathname too long", *argv);
190 (void)strcpy(path, argv[argc - 1]);
191 baselen = strlen(path);
192 endp = &path[baselen];
193#if defined(_MSC_VER) || defined(__EMX__)
194 if (!baselen || (*(endp - 1) != '/' && *(endp - 1) != '\\' && *(endp - 1) != ':')) {
195#else
196 if (!baselen || *(endp - 1) != '/') {
197#endif
198 *endp++ = '/';
199 ++baselen;
200 }
201 for (rval = 0; --argc; ++argv) {
202 /*
203 * Find the last component of the source pathname. It
204 * may have trailing slashes.
205 */
206 p = *argv + strlen(*argv);
207#if defined(_MSC_VER) || defined(__EMX__)
208 while (p != *argv && (p[-1] == '/' || p[-1] == '\\'))
209 --p;
210 while (p != *argv && p[-1] != '/' && p[-1] != '/' && p[-1] != ':')
211 --p;
212#else
213 while (p != *argv && p[-1] == '/')
214 --p;
215 while (p != *argv && p[-1] != '/')
216 --p;
217#endif
218
219 if ((baselen + (len = strlen(p))) >= PATH_MAX) {
220 warnx("%s: destination pathname too long", *argv);
221 rval = 1;
222 } else {
223 memmove(endp, p, (size_t)len + 1);
224 if (do_move(*argv, path))
225 rval = 1;
226 }
227 }
228 return rval;
229}
230
231static int
232do_move(char *from, char *to)
233{
234 struct stat sb;
235 int ask, ch, first;
236 char modep[15];
237
238 /*
239 * Check access. If interactive and file exists, ask user if it
240 * should be replaced. Otherwise if file exists but isn't writable
241 * make sure the user wants to clobber it.
242 */
243 if (!fflg && !access(to, F_OK)) {
244
245 /* prompt only if source exist */
246 if (lstat(from, &sb) == -1) {
247 warn("%s", from);
248 return (1);
249 }
250
251#define YESNO "(y/n [n]) "
252 ask = 0;
253 if (nflg) {
254 if (vflg)
255 printf("%s not overwritten\n", to);
256 return (0);
257 } else if (iflg) {
258 (void)fprintf(stderr, "overwrite %s? %s", to, YESNO);
259 ask = 1;
260 } else if (access(to, W_OK) && !stat(to, &sb)) {
261 strmode(sb.st_mode, modep);
262 (void)fprintf(stderr, "override %s%s%s/%s for %s? %s",
263 modep + 1, modep[9] == ' ' ? "" : " ",
264 user_from_uid((unsigned long)sb.st_uid, 0),
265 group_from_gid((unsigned long)sb.st_gid, 0), to, YESNO);
266 ask = 1;
267 }
268 if (ask) {
269 first = ch = getchar();
270 while (ch != '\n' && ch != EOF)
271 ch = getchar();
272 if (first != 'y' && first != 'Y') {
273 (void)fprintf(stderr, "not overwritten\n");
274 return (0);
275 }
276 }
277 }
278 if (!rename(from, to)) {
279 if (vflg)
280 printf("%s -> %s\n", from, to);
281 return (0);
282 }
283#ifdef _MSC_VER
284 if (errno == EEXIST) {
285 remove(to);
286 if (!rename(from, to)) {
287 if (vflg)
288 printf("%s -> %s\n", from, to);
289 return (0);
290 }
291 }
292#endif
293
294 if (errno == EXDEV) {
295#ifndef CROSS_DEVICE_MOVE
296 warnx("cannot move `%s' to a different device: `%s'", from, to);
297 return (1);
298#else
299 struct statfs sfs;
300 char path[PATH_MAX];
301
302 /*
303 * If the source is a symbolic link and is on another
304 * filesystem, it can be recreated at the destination.
305 */
306 if (lstat(from, &sb) == -1) {
307 warn("%s", from);
308 return (1);
309 }
310 if (!S_ISLNK(sb.st_mode)) {
311 /* Can't mv(1) a mount point. */
312 if (realpath(from, path) == NULL) {
313 warnx("cannot resolve %s: %s", from, path);
314 return (1);
315 }
316 if (!statfs(path, &sfs) &&
317 !strcmp(path, sfs.f_mntonname)) {
318 warnx("cannot rename a mount point");
319 return (1);
320 }
321 }
322#endif
323 } else {
324 warn("rename %s to %s", from, to);
325 return (1);
326 }
327
328#ifdef CROSS_DEVICE_MOVE
329 /*
330 * If rename fails because we're trying to cross devices, and
331 * it's a regular file, do the copy internally; otherwise, use
332 * cp and rm.
333 */
334 if (lstat(from, &sb)) {
335 warn("%s", from);
336 return (1);
337 }
338 return (S_ISREG(sb.st_mode) ?
339 fastcopy(from, to, &sb) : copy(from, to));
340#endif
341}
342
343#ifdef CROSS_DEVICE_MOVE
344int
345static fastcopy(char *from, char *to, struct stat *sbp)
346{
347 struct timeval tval[2];
348 static u_int blen;
349 static char *bp;
350 mode_t oldmode;
351 int nread, from_fd, to_fd;
352 acl_t acl;
353
354 if ((from_fd = open(from, O_RDONLY, 0)) < 0) {
355 warn("%s", from);
356 return (1);
357 }
358 if (blen < sbp->st_blksize) {
359 if (bp != NULL)
360 free(bp);
361 if ((bp = malloc((size_t)sbp->st_blksize)) == NULL) {
362 blen = 0;
363 warnx("malloc failed");
364 return (1);
365 }
366 blen = sbp->st_blksize;
367 }
368 while ((to_fd =
369 open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) {
370 if (errno == EEXIST && unlink(to) == 0)
371 continue;
372 warn("%s", to);
373 (void)close(from_fd);
374 return (1);
375 }
376 while ((nread = read(from_fd, bp, (size_t)blen)) > 0)
377 if (write(to_fd, bp, (size_t)nread) != nread) {
378 warn("%s", to);
379 goto err;
380 }
381 if (nread < 0) {
382 warn("%s", from);
383err: if (unlink(to))
384 warn("%s: remove", to);
385 (void)close(from_fd);
386 (void)close(to_fd);
387 return (1);
388 }
389
390 oldmode = sbp->st_mode & ALLPERMS;
391 if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) {
392 warn("%s: set owner/group (was: %lu/%lu)", to,
393 (u_long)sbp->st_uid, (u_long)sbp->st_gid);
394 if (oldmode & (S_ISUID | S_ISGID)) {
395 warnx(
396"%s: owner/group changed; clearing suid/sgid (mode was 0%03o)",
397 to, oldmode);
398 sbp->st_mode &= ~(S_ISUID | S_ISGID);
399 }
400 }
401 /*
402 * POSIX 1003.2c states that if _POSIX_ACL_EXTENDED is in effect
403 * for dest_file, then it's ACLs shall reflect the ACLs of the
404 * source_file.
405 */
406 if (fpathconf(to_fd, _PC_ACL_EXTENDED) == 1 &&
407 fpathconf(from_fd, _PC_ACL_EXTENDED) == 1) {
408 acl = acl_get_fd(from_fd);
409 if (acl == NULL)
410 warn("failed to get acl entries while setting %s",
411 from);
412 else if (acl_set_fd(to_fd, acl) < 0)
413 warn("failed to set acl entries for %s", to);
414 }
415 (void)close(from_fd);
416 if (fchmod(to_fd, sbp->st_mode))
417 warn("%s: set mode (was: 0%03o)", to, oldmode);
418 /*
419 * XXX
420 * NFS doesn't support chflags; ignore errors unless there's reason
421 * to believe we're losing bits. (Note, this still won't be right
422 * if the server supports flags and we were trying to *remove* flags
423 * on a file that we copied, i.e., that we didn't create.)
424 */
425 errno = 0;
426 if (fchflags(to_fd, (u_long)sbp->st_flags))
427 if (errno != EOPNOTSUPP || sbp->st_flags != 0)
428 warn("%s: set flags (was: 0%07o)", to, sbp->st_flags);
429
430 tval[0].tv_sec = sbp->st_atime;
431 tval[1].tv_sec = sbp->st_mtime;
432 tval[0].tv_usec = tval[1].tv_usec = 0;
433 if (utimes(to, tval))
434 warn("%s: set times", to);
435
436 if (close(to_fd)) {
437 warn("%s", to);
438 return (1);
439 }
440
441 if (unlink(from)) {
442 warn("%s: remove", from);
443 return (1);
444 }
445 if (vflg)
446 printf("%s -> %s\n", from, to);
447 return (0);
448}
449
450int
451copy(char *from, char *to)
452{
453 int pid, status;
454
455 if ((pid = fork()) == 0) {
456 execl(_PATH_CP, "mv", vflg ? "-PRpv" : "-PRp", "--", from, to,
457 (char *)NULL);
458 warn("%s", _PATH_CP);
459 _exit(1);
460 }
461 if (waitpid(pid, &status, 0) == -1) {
462 warn("%s: waitpid", _PATH_CP);
463 return (1);
464 }
465 if (!WIFEXITED(status)) {
466 warnx("%s: did not terminate normally", _PATH_CP);
467 return (1);
468 }
469 if (WEXITSTATUS(status)) {
470 warnx("%s: terminated with %d (non-zero) status",
471 _PATH_CP, WEXITSTATUS(status));
472 return (1);
473 }
474 if (!(pid = vfork())) {
475 execl(_PATH_RM, "mv", "-rf", "--", from, (char *)NULL);
476 warn("%s", _PATH_RM);
477 _exit(1);
478 }
479 if (waitpid(pid, &status, 0) == -1) {
480 warn("%s: waitpid", _PATH_RM);
481 return (1);
482 }
483 if (!WIFEXITED(status)) {
484 warnx("%s: did not terminate normally", _PATH_RM);
485 return (1);
486 }
487 if (WEXITSTATUS(status)) {
488 warnx("%s: terminated with %d (non-zero) status",
489 _PATH_RM, WEXITSTATUS(status));
490 return (1);
491 }
492 return (0);
493}
494#endif /* CROSS_DEVICE_MOVE */
495
496
497static int
498usage(FILE *pf)
499{
500 fprintf(pf, "usage: %s [-f | -i | -n] [-v] source target\n"
501 " or: %s [-f | -i | -n] [-v] source ... directory\n"
502 " or: %s --help\n"
503 " or: %s --version\n");
504 return EX_USAGE;
505}
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