ast@cs.vu.nl (Andy Tanenbaum) (07/26/87)
All previous versions of mv.c barfed on the command: mkdir foo; mv foo bar This one is thought to be better. It should be owned by root with setuid on. Andy Tanenbaum ----------------------------------mv.c------------------------------------- /* mv - move files Author: Adri Koppes * * 4/25/87 - J. Paradis Bug fixes for directory handling */ #include "signal.h" #include "stat.h" int error = 0; struct stat st, st1; main (argc, argv) int argc; char **argv; { char *destdir; if (argc < 3) { std_err ("Usage: mv file1 file2 or mv dir1 dir2 or mv file1 file2 ... dir\n"); exit (1); } if (argc == 3) { if (stat (argv[1], &st)) { std_err ("mv: "); std_err (argv[1]); std_err (" doesn't exist\n"); exit (1); } move (argv[1], argv[2]); } else { destdir = argv[--argc]; if (stat (destdir, &st)) { std_err ("mv: target directory "); std_err (destdir); std_err (" doesn't exist\n"); exit(1); } if ((st.st_mode & S_IFMT) != S_IFDIR) { std_err ("mv: target "); std_err (destdir); std_err (" not a directory\n"); exit (1); } while (--argc) move (*++argv, destdir); } if (error) exit (1); exit(0); } move (old, new) char *old, *new; { int retval; char name[64]; /* It's too dangerous to fool with "." or ".." ! */ if((strcmp(old, ".") == 0) || (strcmp(old, "..") == 0)) { cant(old); } /* Don't move a file to itself. */ if (stat(old, &st)==0 && stat(new, &st1)==0 && st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) cant(old); if (!stat (new, &st)) if((st.st_mode & S_IFMT) != S_IFDIR) unlink (new); else { char *p, *rindex(); if ((strlen(old) + strlen(new) + 2) > 64) { cant(old); error++; return; } strcpy(name, new); strcat(name, "/"); p = rindex(old, '/'); strcat(name, p ? p : old); new = name; } stat (old, &st); if (link (old, new)) if ((st.st_mode & S_IFMT) != S_IFDIR) { switch (fork ()) { case 0: setgid (getgid ()); setuid (getuid ()); execl ("/bin/cp", "cp", old, new, (char *) 0); cant(old); case -1: std_err ("mv: can't fork\n"); exit (1); default: wait (&retval); if (retval) cant(old); } } else cant(old); /* If this was a directory that we moved, then we have ** to update its ".." entry (in case it was moved some- ** where else in the tree...) */ if ((st.st_mode & S_IFMT) == S_IFDIR) { int i; char parent[64], dotdot[64]; strcpy(parent, new); /* Determine the name for the parent of ** the new name by counting back until we ** hit a '/' or the begining of the string */ for(i = (strlen(parent) - 1); i > 0; i--) { if(parent[i] == '/') break; } /* If there are no slashes, then the name is ** in the current directory, so its parent ** is ".". Otherwise, the parent is the name ** up to the last slash. */ if(i == 0) { strcpy(parent, "."); } else { /* null-terminate name at last slash */ parent[i] = '\0'; } /* Unlink the ".." entry */ strcpy(dotdot, new); strcat(dotdot, "/.."); unlink(dotdot); /* Now link it to its parent */ link(parent, dotdot); } utime (new, &st.st_atime); unlink(old); } cant(name) char *name; { std_err("mv: can't move "); std_err (name); std_err ("\n"); exit (1); }