VirtualBox

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

Last change on this file since 3387 was 3247, checked in by bird, 5 years ago

kmkbuiltin: warnings and build fixes

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

© 2023 Oracle
ContactPrivacy policyTerms of Use