mv.c revision 36383
1126277Sdes/* 299052Sdes * Copyright (c) 1989, 1993, 1994 398675Sdes * The Regents of the University of California. All rights reserved. 498675Sdes * 598675Sdes * This code is derived from software contributed to Berkeley by 698675Sdes * Ken Smith of The State University of New York at Buffalo. 798675Sdes * 898675Sdes * Redistribution and use in source and binary forms, with or without 998675Sdes * modification, are permitted provided that the following conditions 1098675Sdes * are met: 1198675Sdes * 1. Redistributions of source code must retain the above copyright 1298675Sdes * notice, this list of conditions and the following disclaimer. 1398675Sdes * 2. Redistributions in binary form must reproduce the above copyright 1498675Sdes * notice, this list of conditions and the following disclaimer in the 1598675Sdes * documentation and/or other materials provided with the distribution. 1698675Sdes * 3. All advertising materials mentioning features or use of this software 1798675Sdes * must display the following acknowledgement: 1898675Sdes * This product includes software developed by the University of 1998675Sdes * California, Berkeley and its contributors. 2098675Sdes * 4. Neither the name of the University nor the names of its contributors 2198675Sdes * may be used to endorse or promote products derived from this software 2298675Sdes * without specific prior written permission. 2398675Sdes * 2498675Sdes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 2598675Sdes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 2698675Sdes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 2798675Sdes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 2898675Sdes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 2998675Sdes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 3098675Sdes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 3198675Sdes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 3298675Sdes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 3398675Sdes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 3498675Sdes * SUCH DAMAGE. 3598675Sdes */ 3698675Sdes 3798675Sdes#ifndef lint 3898675Sdesstatic char const copyright[] = 3998675Sdes"@(#) Copyright (c) 1989, 1993, 1994\n\ 4098675Sdes The Regents of the University of California. All rights reserved.\n"; 4198675Sdes#endif /* not lint */ 4298675Sdes 4398675Sdes#ifndef lint 44126277Sdes#if 0 4598675Sdesstatic char sccsid[] = "@(#)mv.c 8.2 (Berkeley) 4/2/94"; 4698675Sdes#endif 4798675Sdesstatic const char rcsid[] = 4898675Sdes "$Id: mv.c,v 1.18 1998/05/15 06:25:17 charnier Exp $"; 49106130Sdes#endif /* not lint */ 5098675Sdes 5198675Sdes#include <sys/param.h> 5298675Sdes#include <sys/time.h> 5398675Sdes#include <sys/wait.h> 5498675Sdes#include <sys/stat.h> 5598675Sdes#include <sys/mount.h> 5698675Sdes 5798675Sdes#include <err.h> 5898675Sdes#include <errno.h> 5998675Sdes#include <fcntl.h> 60124211Sdes#include <stdio.h> 61124211Sdes#include <stdlib.h> 62124211Sdes#include <string.h> 63124211Sdes#include <unistd.h> 64124211Sdes 65124211Sdes#include "pathnames.h" 66126277Sdes 67124211Sdesint fflg, iflg; 68124211Sdes 6998937Sdesint copy __P((char *, char *)); 7098937Sdesint do_move __P((char *, char *)); 71124211Sdesint fastcopy __P((char *, char *, struct stat *)); 72124211Sdesvoid usage __P((void)); 73124211Sdes 74124211Sdesint 75124211Sdesmain(argc, argv) 7698937Sdes int argc; 7798937Sdes char *argv[]; 78126277Sdes{ 7998675Sdes register int baselen, len, rval; 8098675Sdes register char *p, *endp; 81126277Sdes struct stat sb; 8298675Sdes int ch; 8398675Sdes char path[MAXPATHLEN + 1]; 8498675Sdes 8598675Sdes while ((ch = getopt(argc, argv, "fi")) != -1) 8698675Sdes switch (ch) { 8798675Sdes case 'i': 8898675Sdes iflg = 1; 8998675Sdes fflg = 0; 9098675Sdes break; 9198675Sdes case 'f': 9298675Sdes fflg = 1; 9398675Sdes iflg = 0; 9498675Sdes break; 9598675Sdes default: 9698675Sdes usage(); 9798675Sdes } 9898675Sdes argc -= optind; 9998675Sdes argv += optind; 10098675Sdes 10198675Sdes if (argc < 2) 10298675Sdes usage(); 10398675Sdes 10498675Sdes /* 10598675Sdes * If the stat on the target fails or the target isn't a directory, 10698675Sdes * try the move. More than 2 arguments is an error in this case. 10798675Sdes */ 10898675Sdes if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { 10998675Sdes if (argc > 2) 110 usage(); 111 exit(do_move(argv[0], argv[1])); 112 } 113 114 /* It's a directory, move each file into it. */ 115 (void)strcpy(path, argv[argc - 1]); 116 baselen = strlen(path); 117 endp = &path[baselen]; 118 if (!baselen || *(endp - 1) != '/') { 119 *endp++ = '/'; 120 ++baselen; 121 } 122 for (rval = 0; --argc; ++argv) { 123 /* 124 * Find the last component of the source pathname. It 125 * may have trailing slashes. 126 */ 127 p = *argv + strlen(*argv); 128 while (p != *argv && p[-1] == '/') 129 --p; 130 while (p != *argv && p[-1] != '/') 131 --p; 132 133 if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { 134 warnx("%s: destination pathname too long", *argv); 135 rval = 1; 136 } else { 137 memmove(endp, p, len + 1); 138 if (do_move(*argv, path)) 139 rval = 1; 140 } 141 } 142 exit(rval); 143} 144 145int 146do_move(from, to) 147 char *from, *to; 148{ 149 struct stat sb; 150 int ask, ch, first; 151 char modep[15]; 152 153 /* 154 * Check access. If interactive and file exists, ask user if it 155 * should be replaced. Otherwise if file exists but isn't writable 156 * make sure the user wants to clobber it. 157 */ 158 if (!fflg && !access(to, F_OK)) { 159 160 /* prompt only if source exist */ 161 if (lstat(from, &sb) == -1) { 162 warn("%s", from); 163 return (1); 164 } 165 166#define YESNO "(y/n [n]) " 167 ask = 0; 168 if (iflg) { 169 (void)fprintf(stderr, "overwrite %s? %s", to, YESNO); 170 ask = 1; 171 } else if (access(to, W_OK) && !stat(to, &sb)) { 172 strmode(sb.st_mode, modep); 173 (void)fprintf(stderr, "override %s%s%s/%s for %s? %s", 174 modep + 1, modep[9] == ' ' ? "" : " ", 175 user_from_uid(sb.st_uid, 0), 176 group_from_gid(sb.st_gid, 0), to, YESNO); 177 ask = 1; 178 } 179 if (ask) { 180 first = ch = getchar(); 181 while (ch != '\n' && ch != EOF) 182 ch = getchar(); 183 if (first != 'y' && first != 'Y') { 184 (void)fprintf(stderr, "not overwritten\n"); 185 return (0); 186 } 187 } 188 } 189 if (!rename(from, to)) 190 return (0); 191 192 if (errno == EXDEV) { 193 struct statfs sfs; 194 char path[MAXPATHLEN]; 195 196 /* Can't mv(1) a mount point. */ 197 if (realpath(from, path) == NULL) { 198 warnx("cannot resolve %s: %s", from, path); 199 return (1); 200 } 201 if (!statfs(path, &sfs) && !strcmp(path, sfs.f_mntonname)) { 202 warnx("cannot rename a mount point"); 203 return (1); 204 } 205 } else { 206 warn("rename %s to %s", from, to); 207 return (1); 208 } 209 210 /* 211 * If rename fails because we're trying to cross devices, and 212 * it's a regular file, do the copy internally; otherwise, use 213 * cp and rm. 214 */ 215 if (stat(from, &sb)) { 216 warn("%s", from); 217 return (1); 218 } 219 return (S_ISREG(sb.st_mode) ? 220 fastcopy(from, to, &sb) : copy(from, to)); 221} 222 223int 224fastcopy(from, to, sbp) 225 char *from, *to; 226 struct stat *sbp; 227{ 228 struct timeval tval[2]; 229 static u_int blen; 230 static char *bp; 231 mode_t oldmode; 232 register int nread, from_fd, to_fd; 233 234 if ((from_fd = open(from, O_RDONLY, 0)) < 0) { 235 warn("%s", from); 236 return (1); 237 } 238 if (blen < sbp->st_blksize) { 239 if (bp != NULL) 240 free(bp); 241 if ((bp = malloc(sbp->st_blksize)) == NULL) { 242 blen = 0; 243 warnx("malloc failed"); 244 return (1); 245 } 246 blen = sbp->st_blksize; 247 } 248 while ((to_fd = 249 open(to, O_CREAT | O_EXCL | O_TRUNC | O_WRONLY, 0)) < 0) { 250 if (errno == EEXIST && unlink(to) == 0) 251 continue; 252 warn("%s", to); 253 (void)close(from_fd); 254 return (1); 255 } 256 while ((nread = read(from_fd, bp, blen)) > 0) 257 if (write(to_fd, bp, nread) != nread) { 258 warn("%s", to); 259 goto err; 260 } 261 if (nread < 0) { 262 warn("%s", from); 263err: if (unlink(to)) 264 warn("%s: remove", to); 265 (void)close(from_fd); 266 (void)close(to_fd); 267 return (1); 268 } 269 (void)close(from_fd); 270 271 oldmode = sbp->st_mode & ALLPERMS; 272 if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) { 273 warn("%s: set owner/group (was: %u/%u)", to, sbp->st_uid, 274 sbp->st_gid); 275 if (oldmode & (S_ISUID | S_ISGID)) { 276 warnx( 277"%s: owner/group changed; clearing suid/sgid (mode was 0%03o)", 278 to, oldmode); 279 sbp->st_mode &= ~(S_ISUID | S_ISGID); 280 } 281 } 282 if (fchmod(to_fd, sbp->st_mode)) 283 warn("%s: set mode (was: 0%03o)", to, oldmode); 284 285 tval[0].tv_sec = sbp->st_atime; 286 tval[1].tv_sec = sbp->st_mtime; 287 tval[0].tv_usec = tval[1].tv_usec = 0; 288 if (utimes(to, tval)) 289 warn("%s: set times", to); 290 291 if (close(to_fd)) { 292 warn("%s", to); 293 return (1); 294 } 295 296 if (unlink(from)) { 297 warn("%s: remove", from); 298 return (1); 299 } 300 return (0); 301} 302 303int 304copy(from, to) 305 char *from, *to; 306{ 307 int pid, status; 308 309 if ((pid = vfork()) == 0) { 310 execl(_PATH_CP, "mv", "-PRp", from, to, NULL); 311 warn("%s", _PATH_CP); 312 _exit(1); 313 } 314 if (waitpid(pid, &status, 0) == -1) { 315 warn("%s: waitpid", _PATH_CP); 316 return (1); 317 } 318 if (!WIFEXITED(status)) { 319 warn("%s: did not terminate normally", _PATH_CP); 320 return (1); 321 } 322 if (WEXITSTATUS(status)) { 323 warn("%s: terminated with %d (non-zero) status", 324 _PATH_CP, WEXITSTATUS(status)); 325 return (1); 326 } 327 if (!(pid = vfork())) { 328 execl(_PATH_RM, "mv", "-rf", from, NULL); 329 warn("%s", _PATH_RM); 330 _exit(1); 331 } 332 if (waitpid(pid, &status, 0) == -1) { 333 warn("%s: waitpid", _PATH_RM); 334 return (1); 335 } 336 if (!WIFEXITED(status)) { 337 warn("%s: did not terminate normally", _PATH_RM); 338 return (1); 339 } 340 if (WEXITSTATUS(status)) { 341 warn("%s: terminated with %d (non-zero) status", 342 _PATH_RM, WEXITSTATUS(status)); 343 return (1); 344 } 345 return (0); 346} 347 348void 349usage() 350{ 351 (void)fprintf(stderr, "%s\n%s\n", 352 "usage: mv [-f | -i] source target", 353 " mv [-f | -i] source ... directory"); 354 exit(1); 355} 356