X-Git-Url: https://git.subgeniuskitty.com/unix-history/.git/blobdiff_plain/2a7e674d556087289ce015ebd1befb1b5eab1fd2..dfb144fcc76595bb53320c5019330998b9103033:/usr/src/bin/mv/mv.c diff --git a/usr/src/bin/mv/mv.c b/usr/src/bin/mv/mv.c index a5847ae689..1df3aafd48 100644 --- a/usr/src/bin/mv/mv.c +++ b/usr/src/bin/mv/mv.c @@ -1,354 +1,274 @@ -static char *sccsid = "@(#)mv.c 4.6 (Berkeley) 82/10/23"; - /* - * mv file1 file2 + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Ken Smith of The State University of New York at Buffalo. + * + * %sccs.include.redist.c% */ -#include -#include -#include -#include +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ -#define DOT "." -#define DOTDOT ".." -#define DELIM '/' -#define SDELIM "/" -#define MAXN 300 -#define MODEBITS 07777 -#define ROOTINO 2 +#ifndef lint +static char sccsid[] = "@(#)mv.c 8.2 (Berkeley) %G%"; +#endif /* not lint */ -char *pname(); -char *sprintf(); -char *dname(); -struct stat s1, s2; -int iflag = 0; /* interactive flag. If this flag is set, - * the user is queried before files are - * destroyed by cp. - */ -int fflag = 0; /* force flag. supercedes all restrictions */ +#include +#include +#include +#include -main(argc, argv) -register char *argv[]; -{ - register i, r; - register char *arg; +#include +#include +#include +#include +#include +#include +#include - /* get the flag(s) */ +#include "pathnames.h" - if (argc < 2) - goto usage; - while (argc>1 && *argv[1] == '-') { - argc--; - arg = *++argv; +int fflg, iflg; - /* - * all files following a null option are considered file names - */ - if (*(arg+1) == '\0') break; +int copy __P((char *, char *)); +int do_move __P((char *, char *)); +int fastcopy __P((char *, char *, struct stat *)); +void usage __P((void)); - while (*++arg != '\0') - switch (*arg) { +int +main(argc, argv) + int argc; + char *argv[]; +{ + register int baselen, len, rval; + register char *p, *endp; + struct stat sb; + int ch; + char path[MAXPATHLEN + 1]; - /* interactive mode */ - case 'i': - iflag++; - break; + while ((ch = getopt(argc, argv, "-if")) != EOF) + switch (ch) { + case 'i': + iflg = 1; + break; + case 'f': + fflg = 1; + break; + case '-': /* Undocumented; for compatibility. */ + goto endarg; + case '?': + default: + usage(); + } +endarg: argc -= optind; + argv += optind; - /* force moves */ - case 'f': - fflag++; - break; + if (argc < 2) + usage(); - /* don't live with bad options */ - default: - goto usage; - } - } - if (argc < 3) - goto usage; - if (lstat(argv[1], &s1) < 0) { - fprintf(stderr, "mv: cannot access %s\n", argv[1]); - return(1); + /* + * If the stat on the target fails or the target isn't a directory, + * try the move. More than 2 arguments is an error in this case. + */ + if (stat(argv[argc - 1], &sb) || !S_ISDIR(sb.st_mode)) { + if (argc > 2) + usage(); + exit(do_move(argv[0], argv[1])); } - if ((s1.st_mode & S_IFMT) == S_IFDIR) { - if (argc != 3) - goto usage; - return mvdir(argv[1], argv[2]); + + /* It's a directory, move each file into it. */ + (void)strcpy(path, argv[argc - 1]); + baselen = strlen(path); + endp = &path[baselen]; + *endp++ = '/'; + ++baselen; + for (rval = 0; --argc; ++argv) { + if ((p = strrchr(*argv, '/')) == NULL) + p = *argv; + else + ++p; + if ((baselen + (len = strlen(p))) >= MAXPATHLEN) { + warnx("%s: destination pathname too long", *argv); + rval = 1; + } else { + memmove(endp, p, len + 1); + if (do_move(*argv, path)) + rval = 1; + } } - setuid(getuid()); - if (argc > 3) - if (stat(argv[argc-1], &s2) < 0 || (s2.st_mode & S_IFMT) != S_IFDIR) - goto usage; - r = 0; - for (i=1; i= 0) { - if ((s2.st_mode & S_IFMT) == S_IFDIR) { - sprintf(buf, "%s/%s", target, dname(source)); - target = buf; + /* + * Check access. If interactive and file exists, ask user if it + * should be replaced. Otherwise if file exists but isn't writable + * make sure the user wants to clobber it. + */ + if (!fflg && !access(to, F_OK)) { + ask = 0; + if (iflg) { + (void)fprintf(stderr, "overwrite %s? ", to); + ask = 1; + } else if (access(to, W_OK) && !stat(to, &sb)) { + strmode(sb.st_mode, modep); + (void)fprintf(stderr, "override %s%s%s/%s for %s? ", + modep + 1, modep[9] == ' ' ? "" : " ", + user_from_uid(sb.st_uid, 0), + group_from_gid(sb.st_gid, 0), to); + ask = 1; } - if (lstat(target, &s2) >= 0) { - if ((s2.st_mode & S_IFMT) == S_IFDIR) { - fprintf(stderr, "mv: %s is a directory\n", target); - return(1); - } else if (iflag && !fflag) { - fprintf(stderr, "remove %s? ", target); - i = c = getchar(); - while (c != '\n' && c != EOF) - c = getchar(); - if (i != 'y') - return(1); - } - if (s1.st_dev==s2.st_dev && s1.st_ino==s2.st_ino) { - fprintf(stderr, "mv: %s and %s are identical\n", - source, target); - return(1); - } - if (access(target, 2) < 0 && !fflag && isatty(fileno(stdin))) { - fprintf(stderr, "override protection %o for %s? ", - s2.st_mode & MODEBITS, target); - i = c = getchar(); - while (c != '\n' && c != EOF) - c = getchar(); - if (i != 'y') - return(1); - } - if (unlink(target) < 0) { - fprintf(stderr, "mv: cannot unlink %s\n", target); - return(1); - } + if (ask) { + if ((ch = getchar()) != EOF && ch != '\n') + while (getchar() != '\n'); + if (ch != 'y') + return (0); } } - if ((s1.st_mode & S_IFMT) == S_IFLNK) { - register m; - char symln[MAXN]; + if (!rename(from, to)) + return (0); - if (readlink(source, symln, sizeof (symln)) < 0) { - perror(source); - return (1); - } - m = umask(~(s1.st_mode & MODEBITS)); - if (symlink(symln, target) < 0) { - perror(target); - return (1); - } - umask(m); - } else if (link(source, target) < 0) { - i = fork(); - if (i == -1) { - fprintf(stderr, "mv: try again\n"); - return(1); - } - if (i == 0) { - execl("/bin/cp", "cp", source, target, 0); - fprintf(stderr, "mv: cannot exec cp\n"); - exit(1); - } - while ((c = wait(&status)) != i && c != -1) - ; - if (status != 0) - return(1); - utime(target, &s1.st_atime); + if (errno != EXDEV) { + warn("rename %s to %s", from, to); + return (1); } - if (unlink(source) < 0) { - fprintf(stderr, "mv: cannot unlink %s\n", source); - return(1); + + /* + * If rename fails because we're trying to cross devices, and + * it's a regular file, do the copy internally; otherwise, use + * cp and rm. + */ + if (stat(from, &sb)) { + warn("%s", from); + return (1); } - return(0); + return (S_ISREG(sb.st_mode) ? + fastcopy(from, to, &sb) : copy(from, to)); } -mvdir(source, target) -char *source, *target; +int +fastcopy(from, to, sbp) + char *from, *to; + struct stat *sbp; { - register char *p; - register i; - char buf[MAXN]; - char c,cc; + struct timeval tval[2]; + static u_int blen; + static char *bp; + register int nread, from_fd, to_fd; - if (stat(target, &s2) >= 0) { - if ((s2.st_mode&S_IFMT) != S_IFDIR) { - fprintf(stderr, "mv: %s exists\n", target); - return(1); - } - p = dname(source); - if (strlen(target) > MAXN-strlen(p)-2) { - fprintf(stderr, "mv :target name too long\n"); - return(1); - } - strcpy(buf, target); - target = buf; - strcat(buf, SDELIM); - strcat(buf, p); - if (stat(target, &s2) >= 0) { - fprintf(stderr, "mv: %s exists\n", buf); - return(1); - } - } - if (strcmp(source, target) == 0) { - fprintf(stderr, "mv: ?? source == target, source exists and target doesnt\n"); - return(1); - } - p = dname(source); - if (!strcmp(p, DOT) || !strcmp(p, DOTDOT) || !strcmp(p, "") || p[strlen(p)-1]=='/') { - fprintf(stderr, "mv: cannot rename %s\n", p); - return(1); + if ((from_fd = open(from, O_RDONLY, 0)) < 0) { + warn("%s", from); + return (1); } - if (stat(pname(source), &s1) < 0 || stat(pname(target), &s2) < 0) { - fprintf(stderr, "mv: cannot locate parent\n"); - return(1); + if ((to_fd = + open(to, O_CREAT | O_TRUNC | O_WRONLY, sbp->st_mode)) < 0) { + warn("%s", to); + (void)close(from_fd); + return (1); } - if (access(pname(target), 2) < 0) { - fprintf(stderr, "mv: no write access to %s\n", pname(target)); - return(1); + if (!blen && !(bp = malloc(blen = sbp->st_blksize))) { + warn(NULL); + return (1); } - if (access(pname(source), 2) < 0) { - fprintf(stderr, "mv: no write access to %s\n", pname(source)); - return(1); - } - if (s1.st_dev != s2.st_dev) { - fprintf(stderr, "mv: cannot move directories across devices\n"); - return(1); - } - if (s1.st_ino != s2.st_ino) { - char dst[MAXN+5]; - - if (chkdot(source) || chkdot(target)) { - fprintf(stderr, "mv: Sorry, path names including %s aren't allowed\n", DOTDOT); - return(1); - } - stat(source, &s1); - if (check(pname(target), s1.st_ino)) - return(1); - for (i = 1; i <= NSIG; i++) - signal(i, SIG_IGN); - if (link(source, target) < 0) { - fprintf(stderr, "mv: cannot link %s to %s\n", target, source); - return(1); - } - if (unlink(source) < 0) { - fprintf(stderr, "mv: %s: cannot unlink\n", source); - unlink(target); - return(1); - } - strcat(dst, target); - strcat(dst, "/"); - strcat(dst, DOTDOT); - if (unlink(dst) < 0) { - fprintf(stderr, "mv: %s: cannot unlink\n", dst); - if (link(target, source) >= 0) - unlink(target); - return(1); - } - if (link(pname(target), dst) < 0) { - fprintf(stderr, "mv: cannot link %s to %s\n", - dst, pname(target)); - if (link(pname(source), dst) >= 0) - if (link(target, source) >= 0) - unlink(target); - return(1); + while ((nread = read(from_fd, bp, blen)) > 0) + if (write(to_fd, bp, nread) != nread) { + warn("%s", to); + goto err; } - return(0); + if (nread < 0) { + warn("%s", from); +err: if (unlink(to)) + warn("%s: remove", to); + (void)close(from_fd); + (void)close(to_fd); + return (1); } - if (link(source, target) < 0) { - fprintf(stderr, "mv: cannot link %s and %s\n", - source, target); - return(1); - } - if (unlink(source) < 0) { - fprintf(stderr, "mv: ?? cannot unlink %s\n", source); - return(1); - } - return(0); -} + (void)close(from_fd); -char * -pname(name) -register char *name; -{ - register c; - register char *p, *q; - static char buf[MAXN]; + if (fchown(to_fd, sbp->st_uid, sbp->st_gid)) + warn("%s: set owner/group", to); + if (fchmod(to_fd, sbp->st_mode)) + warn("%s: set mode", to); - p = q = buf; - while (c = *p++ = *name++) - if (c == DELIM) - q = p-1; - if (q == buf && *q == DELIM) - q++; - *q = 0; - return buf[0]? buf : DOT; -} + tval[0].tv_sec = sbp->st_atime; + tval[1].tv_sec = sbp->st_mtime; + tval[0].tv_usec = tval[1].tv_usec = 0; + if (utimes(to, tval)) + warn("%s: set times", to); -char * -dname(name) -register char *name; -{ - register char *p; + if (close(to_fd)) { + warn("%s", to); + return (1); + } - p = name; - while (*p) - if (*p++ == DELIM && *p) - name = p; - return name; + if (unlink(from)) { + warn("%s: remove", from); + return (1); + } + return (0); } -check(spth, dinode) -char *spth; -ino_t dinode; +int +copy(from, to) + char *from, *to; { - char nspth[MAXN]; - struct stat sbuf; + int pid, status; - sbuf.st_ino = 0; - - strcpy(nspth, spth); - while (sbuf.st_ino != ROOTINO) { - if (stat(nspth, &sbuf) < 0) { - fprintf(stderr, "mv: cannot access %s\n", nspth); - return(1); - } - if (sbuf.st_ino == dinode) { - fprintf(stderr, "mv: cannot move a directory into itself\n"); - return(1); - } - if (strlen(nspth) > MAXN-2-sizeof(DOTDOT)) { - fprintf(stderr, "mv: name too long\n"); - return(1); - } - strcat(nspth, SDELIM); - strcat(nspth, DOTDOT); + if ((pid = vfork()) == 0) { + execl(_PATH_CP, "mv", "-PRp", from, to, NULL); + warn("%s", _PATH_CP); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_CP); + return (1); + } + if (!WIFEXITED(status)) { + warn("%s: did not terminate normally", _PATH_CP); + return (1); + } + if (WEXITSTATUS(status)) { + warn("%s: terminated with %d (non-zero) status", + _PATH_CP, WEXITSTATUS(status)); + return (1); + } + if (!(pid = vfork())) { + execl(_PATH_RM, "mv", "-rf", from, NULL); + warn("%s", _PATH_RM); + _exit(1); + } + if (waitpid(pid, &status, 0) == -1) { + warn("%s: waitpid", _PATH_RM); + return (1); + } + if (!WIFEXITED(status)) { + warn("%s: did not terminate normally", _PATH_RM); + return (1); + } + if (WEXITSTATUS(status)) { + warn("%s: terminated with %d (non-zero) status", + _PATH_RM, WEXITSTATUS(status)); + return (1); } - return(0); + return (0); } -chkdot(s) -register char *s; +void +usage() { - do { - if (strcmp(dname(s), DOTDOT) == 0) - return(1); - s = pname(s); - } while (strcmp(s, DOT) != 0 && strcmp(s, SDELIM) != 0); - return(0); + (void)fprintf(stderr, +"usage: mv [-if] src target;\n or: mv [-if] src1 ... srcN directory\n"); + exit(1); }