mv.c revision 14305
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. 353044Sdg * 3614305Swosch * $Id: mv.c,v 1.7 1996/02/20 23:27:57 wosch Exp $ 371556Srgrimes */ 381556Srgrimes 391556Srgrimes#ifndef lint 401556Srgrimesstatic char copyright[] = 411556Srgrimes"@(#) Copyright (c) 1989, 1993, 1994\n\ 421556Srgrimes The Regents of the University of California. All rights reserved.\n"; 431556Srgrimes#endif /* not lint */ 441556Srgrimes 451556Srgrimes#ifndef lint 461556Srgrimesstatic char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; 471556Srgrimes#endif /* not lint */ 481556Srgrimes 491556Srgrimes#include <sys/param.h> 501556Srgrimes#include <sys/time.h> 511556Srgrimes#include <sys/wait.h> 521556Srgrimes#include <sys/stat.h> 531556Srgrimes 541556Srgrimes#include <err.h> 551556Srgrimes#include <errno.h> 561556Srgrimes#include <fcntl.h> 571556Srgrimes#include <stdio.h> 581556Srgrimes#include <stdlib.h> 591556Srgrimes#include <string.h> 601556Srgrimes#include <unistd.h> 611556Srgrimes 621556Srgrimes#include "pathnames.h" 631556Srgrimes 641556Srgrimesint fflg, iflg; 651556Srgrimes 661556Srgrimesint copy __P((char *, char *)); 671556Srgrimesint do_move __P((char *, char *)); 681556Srgrimesint fastcopy __P((char *, char *, struct stat *)); 691556Srgrimesvoid usage __P((void)); 701556Srgrimes 711556Srgrimesint 721556Srgrimesmain(argc, argv) 731556Srgrimes int argc; 741556Srgrimes char *argv[]; 751556Srgrimes{ 761556Srgrimes register int baselen, len, rval; 771556Srgrimes register char *p, *endp; 781556Srgrimes struct stat sb; 791556Srgrimes int ch; 801556Srgrimes char path[MAXPATHLEN + 1]; 811556Srgrimes 8214305Swosch while ((ch = getopt(argc, argv, "fi")) != EOF) 831556Srgrimes switch (ch) { 841556Srgrimes case 'i': 8514154Swosch iflg = 1; 8614166Swosch fflg = 0; 871556Srgrimes break; 881556Srgrimes case 'f': 891556Srgrimes fflg = 1; 9014166Swosch iflg = 0; 911556Srgrimes break; 921556Srgrimes default: 931556Srgrimes usage(); 941556Srgrimes } 9514305Swosch argc -= optind; 961556Srgrimes argv += optind; 971556Srgrimes 981556Srgrimes if (argc < 2) 991556Srgrimes usage(); 1001556Srgrimes 1011556Srgrimes /* 1021556Srgrimes * If the stat on the target fails or the target isn't a directory, 1031556Srgrimes * try the move. More than 2 arguments is an error in this case. 1041556Srgrimes */ 1051556Srgrimes if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 1061556Srgrimes if (argc > 2) 1071556Srgrimes usage(); 1081556Srgrimes exit(do_move(argv[0], argv[1])); 1091556Srgrimes } 1101556Srgrimes 1111556Srgrimes /* It's a directory, move each file into it. */ 1121556Srgrimes (void)strcpy(path, argv[argc - 1]); 1131556Srgrimes baselen = strlen(path); 1141556Srgrimes endp = &path[baselen]; 1151556Srgrimes *endp++ = '/'; 1161556Srgrimes ++baselen; 1171556Srgrimes for (rval = 0; --argc; ++argv) { 11811298Sbde /* 11911298Sbde * Find the last component of the source pathname. It 12011298Sbde * may have trailing slashes. 12111298Sbde */ 12211298Sbde p = *argv + strlen(*argv); 12311298Sbde while (p != *argv && p[-1] == '/') 12411298Sbde --p; 12511298Sbde while (p != *argv && p[-1] != '/') 12611298Sbde --p; 12711298Sbde 1281556Srgrimes if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 1291556Srgrimes warnx("%s: destination pathname too long", *argv); 1301556Srgrimes rval = 1; 1311556Srgrimes } else { 1321556Srgrimes memmove(endp, p, len + 1); 1331556Srgrimes if (do_move(*argv, path)) 1341556Srgrimes rval = 1; 1351556Srgrimes } 1361556Srgrimes } 1371556Srgrimes exit(rval); 1381556Srgrimes} 1391556Srgrimes 1401556Srgrimesint 1411556Srgrimesdo_move(from, to) 1421556Srgrimes char *from, *to; 1431556Srgrimes{ 1441556Srgrimes struct stat sb; 1451556Srgrimes int ask, ch; 1461556Srgrimes char modep[15]; 1471556Srgrimes 1481556Srgrimes /* 1491556Srgrimes * Check access. If interactive and file exists, ask user if it 1501556Srgrimes * should be replaced. Otherwise if file exists but isn't writable 1511556Srgrimes * make sure the user wants to clobber it. 1521556Srgrimes */ 1531556Srgrimes if (!fflg && !access(to, F_OK)) { 15414166Swosch 15514166Swosch /* prompt only if source exist */ 15614166Swosch if (lstat(from, &sb) == -1) { 15714305Swosch warn("%s", from); 15814305Swosch return (1); 15914166Swosch } 16014166Swosch 1611556Srgrimes ask = 0; 1621556Srgrimes if (iflg) { 1631556Srgrimes (void)fprintf(stderr, "overwrite %s? ", to); 1641556Srgrimes ask = 1; 1651556Srgrimes } else if (access(to, W_OK) && !stat(to, &sb)) { 1661556Srgrimes strmode(sb.st_mode, modep); 1671556Srgrimes (void)fprintf(stderr, "override %s%s%s/%s for %s? ", 1681556Srgrimes modep + 1, modep[9] == ' ' ? "" : " ", 1691556Srgrimes user_from_uid(sb.st_uid, 0), 1701556Srgrimes group_from_gid(sb.st_gid, 0), to); 1711556Srgrimes ask = 1; 1721556Srgrimes } 1731556Srgrimes if (ask) { 1741556Srgrimes if ((ch = getchar()) != EOF && ch != '\n') 1751556Srgrimes while (getchar() != '\n'); 17614166Swosch if (ch != 'y' && ch != 'Y') 1771556Srgrimes return (0); 1781556Srgrimes } 1791556Srgrimes } 1801556Srgrimes if (!rename(from, to)) 1811556Srgrimes return (0); 1821556Srgrimes 1831556Srgrimes if (errno != EXDEV) { 1841556Srgrimes warn("rename %s to %s", from, to); 1851556Srgrimes return (1); 1861556Srgrimes } 1871556Srgrimes 1881556Srgrimes /* 1891556Srgrimes * If rename fails because we're trying to cross devices, and 1901556Srgrimes * it's a regular file, do the copy internally; otherwise, use 1911556Srgrimes * cp and rm. 1921556Srgrimes */ 1931556Srgrimes if (stat(from, &sb)) { 1941556Srgrimes warn("%s", from); 1951556Srgrimes return (1); 1961556Srgrimes } 1971556Srgrimes return (S_ISREG(sb.st_mode) ? 1981556Srgrimes fastcopy(from, to, &sb) : copy(from, to)); 1991556Srgrimes} 2001556Srgrimes 2011556Srgrimesint 2021556Srgrimesfastcopy(from, to, sbp) 2031556Srgrimes char *from, *to; 2041556Srgrimes struct stat *sbp; 2051556Srgrimes{ 2061556Srgrimes struct timeval tval[2]; 2071556Srgrimes static u_int blen; 2081556Srgrimes static char *bp; 2091556Srgrimes register int nread, from_fd, to_fd; 2101556Srgrimes 2111556Srgrimes if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 2121556Srgrimes warn("%s", from); 2131556Srgrimes return (1); 2141556Srgrimes } 2151556Srgrimes if ((to_fd = 2161556Srgrimes open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { 2171556Srgrimes warn("%s", to); 2181556Srgrimes (void)close(from_fd); 2191556Srgrimes return (1); 2201556Srgrimes } 2211556Srgrimes if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { 2221556Srgrimes warn(NULL); 2231556Srgrimes return (1); 2241556Srgrimes } 2251556Srgrimes while ((nread = read(from_fd, bp, blen)) > 0) 2261556Srgrimes if (write(to_fd, bp, nread) != nread) { 2271556Srgrimes warn("%s", to); 2281556Srgrimes goto err; 2291556Srgrimes } 2301556Srgrimes if (nread < 0) { 2311556Srgrimes warn("%s", from); 2321556Srgrimeserr: if (unlink(to)) 2331556Srgrimes warn("%s: remove", to); 2341556Srgrimes (void)close(from_fd); 2351556Srgrimes (void)close(to_fd); 2361556Srgrimes return (1); 2371556Srgrimes } 2381556Srgrimes (void)close(from_fd); 2391556Srgrimes 2401556Srgrimes if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) 2411556Srgrimes warn("%s: set owner/group", to); 2421556Srgrimes if (fchmod(to_fd, sbp->st_mode)) 2431556Srgrimes warn("%s: set mode", to); 2441556Srgrimes 2451556Srgrimes tval[0].tv_sec = sbp->st_atime; 2461556Srgrimes tval[1].tv_sec = sbp->st_mtime; 2471556Srgrimes tval[0].tv_usec = tval[1].tv_usec = 0; 2481556Srgrimes if (utimes(to, tval)) 2491556Srgrimes warn("%s: set times", to); 2501556Srgrimes 2511556Srgrimes if (close(to_fd)) { 2521556Srgrimes warn("%s", to); 2531556Srgrimes return (1); 2541556Srgrimes } 2551556Srgrimes 2561556Srgrimes if (unlink(from)) { 2571556Srgrimes warn("%s: remove", from); 2581556Srgrimes return (1); 2591556Srgrimes } 2601556Srgrimes return (0); 2611556Srgrimes} 2621556Srgrimes 2631556Srgrimesint 2641556Srgrimescopy(from, to) 2651556Srgrimes char *from, *to; 2661556Srgrimes{ 2671556Srgrimes int pid, status; 2681556Srgrimes 2691556Srgrimes if ((pid = vfork()) == 0) { 2701556Srgrimes execl(_PATH_CP, "mv", "-PRp", from, to, NULL); 2711556Srgrimes warn("%s", _PATH_CP); 2721556Srgrimes _exit(1); 2731556Srgrimes } 2741556Srgrimes if (waitpid(pid, &status, 0) == -1) { 2751556Srgrimes warn("%s: waitpid", _PATH_CP); 2761556Srgrimes return (1); 2771556Srgrimes } 2781556Srgrimes if (!WIFEXITED(status)) { 2791556Srgrimes warn("%s: did not terminate normally", _PATH_CP); 2801556Srgrimes return (1); 2811556Srgrimes } 2821556Srgrimes if (WEXITSTATUS(status)) { 2831556Srgrimes warn("%s: terminated with %d (non-zero) status", 2841556Srgrimes _PATH_CP, WEXITSTATUS(status)); 2851556Srgrimes return (1); 2861556Srgrimes } 2871556Srgrimes if (!(pid = vfork())) { 2881556Srgrimes execl(_PATH_RM, "mv", "-rf", from, NULL); 2891556Srgrimes warn("%s", _PATH_RM); 2901556Srgrimes _exit(1); 2911556Srgrimes } 2921556Srgrimes if (waitpid(pid, &status, 0) == -1) { 2931556Srgrimes warn("%s: waitpid", _PATH_RM); 2941556Srgrimes return (1); 2951556Srgrimes } 2961556Srgrimes if (!WIFEXITED(status)) { 2971556Srgrimes warn("%s: did not terminate normally", _PATH_RM); 2981556Srgrimes return (1); 2991556Srgrimes } 3001556Srgrimes if (WEXITSTATUS(status)) { 3011556Srgrimes warn("%s: terminated with %d (non-zero) status", 3021556Srgrimes _PATH_RM, WEXITSTATUS(status)); 3031556Srgrimes return (1); 3041556Srgrimes } 3051556Srgrimes return (0); 3061556Srgrimes} 3071556Srgrimes 3081556Srgrimesvoid 3091556Srgrimesusage() 3101556Srgrimes{ 31114305Swosch (void)fprintf(stderr, "%s\n%s\n", 31214305Swosch "usage: mv [-f | -i] src target", 31314305Swosch " mv [-f | -i] src1 ... srcN directory"); 3141556Srgrimes exit(1); 3151556Srgrimes} 316