mv.c revision 36383
11556Srgrimes/* 21556Srgrimes * Copyright (c) 1989, 1993, 1994 31556Srgrimes * The Regents of the University of California. All rights reserved. 41556Srgrimes * 51556Srgrimes * This code is derived from software contributed to Berkeley by 61556Srgrimes * Ken Smith of The State University of New York at Buffalo. 71556Srgrimes * 81556Srgrimes * Redistribution and use in source and binary forms, with or without 91556Srgrimes * modification, are permitted provided that the following conditions 101556Srgrimes * are met: 111556Srgrimes * 1. Redistributions of source code must retain the above copyright 121556Srgrimes * notice, this list of conditions and the following disclaimer. 131556Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141556Srgrimes * notice, this list of conditions and the following disclaimer in the 151556Srgrimes * documentation and/or other materials provided with the distribution. 161556Srgrimes * 3. All advertising materials mentioning features or use of this software 171556Srgrimes * must display the following acknowledgement: 181556Srgrimes * This product includes software developed by the University of 191556Srgrimes * California, Berkeley and its contributors. 201556Srgrimes * 4. Neither the name of the University nor the names of its contributors 211556Srgrimes * may be used to endorse or promote products derived from this software 221556Srgrimes * without specific prior written permission. 231556Srgrimes * 241556Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251556Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261556Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271556Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281556Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291556Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301556Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311556Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321556Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331556Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341556Srgrimes * SUCH DAMAGE. 351556Srgrimes */ 361556Srgrimes 371556Srgrimes#ifndef lint 3820420Sstevestatic char const copyright[] = 391556Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 401556Srgrimes The Regents of the University of California. All rights reserved.\n"; 411556Srgrimes#endif /* not lint */ 421556Srgrimes 431556Srgrimes#ifndef lint 4436049Scharnier#if 0 4536049Scharnierstatic char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; 4636049Scharnier#endif 4736049Scharnierstatic const char rcsid[] = 4836383Ssteve "$Id: mv.c,v 1.18 1998/05/15 06:25:17 charnier Exp $"; 491556Srgrimes#endif /* not lint */ 501556Srgrimes 511556Srgrimes#include <sys/param.h> 521556Srgrimes#include <sys/time.h> 531556Srgrimes#include <sys/wait.h> 541556Srgrimes#include <sys/stat.h> 5531664Seivind#include <sys/mount.h> 561556Srgrimes 571556Srgrimes#include <err.h> 581556Srgrimes#include <errno.h> 591556Srgrimes#include <fcntl.h> 601556Srgrimes#include <stdio.h> 611556Srgrimes#include <stdlib.h> 621556Srgrimes#include <string.h> 631556Srgrimes#include <unistd.h> 641556Srgrimes 651556Srgrimes#include "pathnames.h" 661556Srgrimes 671556Srgrimesint fflg, iflg; 681556Srgrimes 691556Srgrimesint copy __P((char *, char *)); 701556Srgrimesint do_move __P((char *, char *)); 711556Srgrimesint fastcopy __P((char *, char *, struct stat *)); 721556Srgrimesvoid usage __P((void)); 731556Srgrimes 741556Srgrimesint 751556Srgrimesmain(argc, argv) 761556Srgrimes int argc; 771556Srgrimes char *argv[]; 781556Srgrimes{ 791556Srgrimes register int baselen, len, rval; 801556Srgrimes register char *p, *endp; 811556Srgrimes struct stat sb; 821556Srgrimes int ch; 831556Srgrimes char path[MAXPATHLEN + 1]; 841556Srgrimes 8524348Simp while ((ch = getopt(argc, argv, "fi")) != -1) 861556Srgrimes switch (ch) { 871556Srgrimes case 'i': 8814154Swosch iflg = 1; 8914166Swosch fflg = 0; 901556Srgrimes break; 911556Srgrimes case 'f': 921556Srgrimes fflg = 1; 9314166Swosch iflg = 0; 941556Srgrimes break; 951556Srgrimes default: 961556Srgrimes usage(); 971556Srgrimes } 9814305Swosch argc -= optind; 991556Srgrimes argv += optind; 1001556Srgrimes 1011556Srgrimes if (argc < 2) 1021556Srgrimes usage(); 1031556Srgrimes 1041556Srgrimes /* 1051556Srgrimes * If the stat on the target fails or the target isn't a directory, 1061556Srgrimes * try the move. More than 2 arguments is an error in this case. 1071556Srgrimes */ 1081556Srgrimes if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 1091556Srgrimes if (argc > 2) 1101556Srgrimes usage(); 1111556Srgrimes exit(do_move(argv[0], argv[1])); 1121556Srgrimes } 1131556Srgrimes 1141556Srgrimes /* It's a directory, move each file into it. */ 1151556Srgrimes (void)strcpy(path, argv[argc - 1]); 1161556Srgrimes baselen = strlen(path); 1171556Srgrimes endp = &path[baselen]; 11836383Ssteve if (!baselen || *(endp - 1) != '/') { 11936383Ssteve *endp++ = '/'; 12036383Ssteve ++baselen; 12136383Ssteve } 1221556Srgrimes for (rval = 0; --argc; ++argv) { 12311298Sbde /* 12411298Sbde * Find the last component of the source pathname. It 12511298Sbde * may have trailing slashes. 12611298Sbde */ 12711298Sbde p = *argv + strlen(*argv); 12811298Sbde while (p != *argv && p[-1] == '/') 12911298Sbde --p; 13011298Sbde while (p != *argv && p[-1] != '/') 13111298Sbde --p; 13211298Sbde 1331556Srgrimes if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 1341556Srgrimes warnx("%s: destination pathname too long", *argv); 1351556Srgrimes rval = 1; 1361556Srgrimes } else { 1371556Srgrimes memmove(endp, p, len + 1); 1381556Srgrimes if (do_move(*argv, path)) 1391556Srgrimes rval = 1; 1401556Srgrimes } 1411556Srgrimes } 1421556Srgrimes exit(rval); 1431556Srgrimes} 1441556Srgrimes 1451556Srgrimesint 1461556Srgrimesdo_move(from, to) 1471556Srgrimes char *from, *to; 1481556Srgrimes{ 1491556Srgrimes struct stat sb; 15029933Swosch int ask, ch, first; 1511556Srgrimes char modep[15]; 1521556Srgrimes 1531556Srgrimes /* 1541556Srgrimes * Check access. If interactive and file exists, ask user if it 1551556Srgrimes * should be replaced. Otherwise if file exists but isn't writable 1561556Srgrimes * make sure the user wants to clobber it. 1571556Srgrimes */ 1581556Srgrimes if (!fflg && !access(to, F_OK)) { 15914166Swosch 16014166Swosch /* prompt only if source exist */ 16114166Swosch if (lstat(from, &sb) == -1) { 16214305Swosch warn("%s", from); 16314305Swosch return (1); 16414166Swosch } 16530106Swosch 16630106Swosch#define YESNO "(y/n [n]) " 1671556Srgrimes ask = 0; 1681556Srgrimes if (iflg) { 16930106Swosch (void)fprintf(stderr, "overwrite %s? %s", to, YESNO); 1701556Srgrimes ask = 1; 1711556Srgrimes } else if (access(to, W_OK) && !stat(to, &sb)) { 1721556Srgrimes strmode(sb.st_mode, modep); 17330106Swosch (void)fprintf(stderr, "override %s%s%s/%s for %s? %s", 1741556Srgrimes modep + 1, modep[9] == ' ' ? "" : " ", 1751556Srgrimes user_from_uid(sb.st_uid, 0), 17630106Swosch group_from_gid(sb.st_gid, 0), to, YESNO); 1771556Srgrimes ask = 1; 1781556Srgrimes } 1791556Srgrimes if (ask) { 18029933Swosch first = ch = getchar(); 18129933Swosch while (ch != '\n' && ch != EOF) 18229933Swosch ch = getchar(); 18330106Swosch if (first != 'y' && first != 'Y') { 18430106Swosch (void)fprintf(stderr, "not overwritten\n"); 1851556Srgrimes return (0); 18630106Swosch } 1871556Srgrimes } 1881556Srgrimes } 1891556Srgrimes if (!rename(from, to)) 1901556Srgrimes return (0); 1911556Srgrimes 19231664Seivind if (errno == EXDEV) { 19331664Seivind struct statfs sfs; 19431664Seivind char path[MAXPATHLEN]; 19531664Seivind 19631664Seivind /* Can't mv(1) a mount point. */ 19731664Seivind if (realpath(from, path) == NULL) { 19831664Seivind warnx("cannot resolve %s: %s", from, path); 19931664Seivind return (1); 20031664Seivind } 20131664Seivind if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) { 20231664Seivind warnx("cannot rename a mount point"); 20331664Seivind return (1); 20431664Seivind } 20531664Seivind } else { 2061556Srgrimes warn("rename %s to %s", from, to); 2071556Srgrimes return (1); 2081556Srgrimes } 2091556Srgrimes 2101556Srgrimes /* 2111556Srgrimes * If rename fails because we're trying to cross devices, and 2121556Srgrimes * it's a regular file, do the copy internally; otherwise, use 2131556Srgrimes * cp and rm. 2141556Srgrimes */ 2151556Srgrimes if (stat(from, &sb)) { 2161556Srgrimes warn("%s", from); 2171556Srgrimes return (1); 2181556Srgrimes } 2191556Srgrimes return (S_ISREG(sb.st_mode) ? 2201556Srgrimes fastcopy(from, to, &sb) : copy(from, to)); 2211556Srgrimes} 2221556Srgrimes 2231556Srgrimesint 2241556Srgrimesfastcopy(from, to, sbp) 2251556Srgrimes char *from, *to; 2261556Srgrimes struct stat *sbp; 2271556Srgrimes{ 2281556Srgrimes struct timeval tval[2]; 2291556Srgrimes static u_int blen; 2301556Srgrimes static char *bp; 23123525Sguido mode_t oldmode; 2321556Srgrimes register int nread, from_fd, to_fd; 2331556Srgrimes 2341556Srgrimes if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 2351556Srgrimes warn("%s", from); 2361556Srgrimes return (1); 2371556Srgrimes } 23823525Sguido if (blen < sbp->st_blksize) { 23923525Sguido if (bp != NULL) 24023525Sguido free(bp); 24123525Sguido if ((bp = malloc(sbp->st_blksize)) == NULL) { 24223525Sguido blen = 0; 24323525Sguido warnx("malloc failed"); 24423525Sguido return (1); 24523525Sguido } 24623525Sguido blen = sbp->st_blksize; 24723525Sguido } 24823525Sguido while ((to_fd = 24923525Sguido open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { 25023525Sguido if (errno == EEXIST && unlink(to) == 0) 25123525Sguido continue; 2521556Srgrimes warn("%s", to); 2531556Srgrimes (void)close(from_fd); 2541556Srgrimes return (1); 2551556Srgrimes } 2561556Srgrimes while ((nread = read(from_fd, bp, blen)) > 0) 2571556Srgrimes if (write(to_fd, bp, nread) != nread) { 2581556Srgrimes warn("%s", to); 2591556Srgrimes goto err; 2601556Srgrimes } 2611556Srgrimes if (nread < 0) { 2621556Srgrimes warn("%s", from); 2631556Srgrimeserr: if (unlink(to)) 2641556Srgrimes warn("%s: remove", to); 2651556Srgrimes (void)close(from_fd); 2661556Srgrimes (void)close(to_fd); 2671556Srgrimes return (1); 2681556Srgrimes } 2691556Srgrimes (void)close(from_fd); 2701556Srgrimes 27123525Sguido oldmode = sbp->st_mode & ALLPERMS; 27223525Sguido if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { 27323525Sguido warn("%s: set owner/group (was: %u/%u)", to, sbp->st_uid, 27423525Sguido sbp->st_gid); 27523525Sguido if (oldmode & (S_ISUID | S_ISGID)) { 27623525Sguido warnx( 27723525Sguido"%s: owner/group changed; clearing suid/sgid (mode was 0%03o)", 27823525Sguido to, oldmode); 27923525Sguido sbp->st_mode &= ~(S_ISUID | S_ISGID); 28023525Sguido } 28123525Sguido } 2821556Srgrimes if (fchmod(to_fd, sbp->st_mode)) 28323525Sguido warn("%s: set mode (was: 0%03o)", to, oldmode); 2841556Srgrimes 2851556Srgrimes tval[0].tv_sec = sbp->st_atime; 2861556Srgrimes tval[1].tv_sec = sbp->st_mtime; 2871556Srgrimes tval[0].tv_usec = tval[1].tv_usec = 0; 2881556Srgrimes if (utimes(to, tval)) 2891556Srgrimes warn("%s: set times", to); 2901556Srgrimes 2911556Srgrimes if (close(to_fd)) { 2921556Srgrimes warn("%s", to); 2931556Srgrimes return (1); 2941556Srgrimes } 2951556Srgrimes 2961556Srgrimes if (unlink(from)) { 2971556Srgrimes warn("%s: remove", from); 2981556Srgrimes return (1); 2991556Srgrimes } 3001556Srgrimes return (0); 3011556Srgrimes} 3021556Srgrimes 3031556Srgrimesint 3041556Srgrimescopy(from, to) 3051556Srgrimes char *from, *to; 3061556Srgrimes{ 3071556Srgrimes int pid, status; 3081556Srgrimes 3091556Srgrimes if ((pid = vfork()) == 0) { 3101556Srgrimes execl(_PATH_CP, "mv", "-PRp", from, to, NULL); 3111556Srgrimes warn("%s", _PATH_CP); 3121556Srgrimes _exit(1); 3131556Srgrimes } 3141556Srgrimes if (waitpid(pid, &status, 0) == -1) { 3151556Srgrimes warn("%s: waitpid", _PATH_CP); 3161556Srgrimes return (1); 3171556Srgrimes } 3181556Srgrimes if (!WIFEXITED(status)) { 3191556Srgrimes warn("%s: did not terminate normally", _PATH_CP); 3201556Srgrimes return (1); 3211556Srgrimes } 3221556Srgrimes if (WEXITSTATUS(status)) { 3231556Srgrimes warn("%s: terminated with %d (non-zero) status", 3241556Srgrimes _PATH_CP, WEXITSTATUS(status)); 3251556Srgrimes return (1); 3261556Srgrimes } 3271556Srgrimes if (!(pid = vfork())) { 3281556Srgrimes execl(_PATH_RM, "mv", "-rf", from, NULL); 3291556Srgrimes warn("%s", _PATH_RM); 3301556Srgrimes _exit(1); 3311556Srgrimes } 3321556Srgrimes if (waitpid(pid, &status, 0) == -1) { 3331556Srgrimes warn("%s: waitpid", _PATH_RM); 3341556Srgrimes return (1); 3351556Srgrimes } 3361556Srgrimes if (!WIFEXITED(status)) { 3371556Srgrimes warn("%s: did not terminate normally", _PATH_RM); 3381556Srgrimes return (1); 3391556Srgrimes } 3401556Srgrimes if (WEXITSTATUS(status)) { 3411556Srgrimes warn("%s: terminated with %d (non-zero) status", 3421556Srgrimes _PATH_RM, WEXITSTATUS(status)); 3431556Srgrimes return (1); 3441556Srgrimes } 3451556Srgrimes return (0); 3461556Srgrimes} 3471556Srgrimes 3481556Srgrimesvoid 3491556Srgrimesusage() 3501556Srgrimes{ 35114305Swosch (void)fprintf(stderr, "%s\n%s\n", 35230727Shelbig "usage: mv [-f | -i] source target", 35330727Shelbig " mv [-f | -i] source ... directory"); 3541556Srgrimes exit(1); 3551556Srgrimes} 356