VirtualBox

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

Last change on this file since 1162 was 942, checked in by bird, 18 years ago

Always use the GNU make getopt stuff.

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