1*53607Selan #include <stdio.h> 2*53607Selan #include <fts.h> 3*53607Selan #include <stdlib.h> 4*53607Selan 5*53607Selan 6*53607Selan void 7*53607Selan copy_file(fs, dne) 8*53607Selan struct stat *fs; 9*53607Selan int dne; 10*53607Selan { 11*53607Selan static char buf[MAXBSIZE]; 12*53607Selan register int from_fd, to_fd, rcount, wcount; 13*53607Selan struct stat to_stat; 14*53607Selan char *p; 15*53607Selan 16*53607Selan if ((from_fd = open(from.p_path, O_RDONLY, 0)) == -1) { 17*53607Selan err("%s: %s", from.p_path, strerror(errno)); 18*53607Selan return; 19*53607Selan } 20*53607Selan 21*53607Selan /* 22*53607Selan * If the file exists and we're interactive, verify with the user. 23*53607Selan * If the file DNE, set the mode to be the from file, minus setuid 24*53607Selan * bits, modified by the umask; arguably wrong, but it makes copying 25*53607Selan * executables work right and it's been that way forever. (The 26*53607Selan * other choice is 666 or'ed with the execute bits on the from file 27*53607Selan * modified by the umask.) 28*53607Selan */ 29*53607Selan if (!dne) { 30*53607Selan if (iflag) { 31*53607Selan int checkch, ch; 32*53607Selan 33*53607Selan (void)fprintf(stderr, "overwrite %s? ", to.p_path); 34*53607Selan checkch = ch = getchar(); 35*53607Selan while (ch != '\n' && ch != EOF) 36*53607Selan ch = getchar(); 37*53607Selan if (checkch != 'y') { 38*53607Selan (void)close(from_fd); 39*53607Selan return; 40*53607Selan } 41*53607Selan } 42*53607Selan to_fd = open(to.p_path, O_WRONLY|O_TRUNC, 0); 43*53607Selan } else 44*53607Selan to_fd = open(to.p_path, O_WRONLY|O_CREAT|O_TRUNC, 45*53607Selan fs->st_mode & ~(S_ISUID|S_ISGID)); 46*53607Selan 47*53607Selan if (to_fd == -1) { 48*53607Selan err("%s: %s", to.p_path, strerror(errno)); 49*53607Selan (void)close(from_fd); 50*53607Selan return; 51*53607Selan } 52*53607Selan 53*53607Selan /* 54*53607Selan * Mmap and write if less than 8M (the limit is so we don't totally 55*53607Selan * trash memory on big files. This is really a minor hack, but it 56*53607Selan * wins some CPU back. 57*53607Selan */ 58*53607Selan if (fs->st_size <= 8 * 1048576) { 59*53607Selan if ((p = mmap(NULL, fs->st_size, PROT_READ, 60*53607Selan MAP_FILE, from_fd, (off_t)0)) == (char *)-1) 61*53607Selan err("%s: %s", from.p_path, strerror(errno)); 62*53607Selan if (write(to_fd, p, fs->st_size) != fs->st_size) 63*53607Selan err("%s: %s", to.p_path, strerror(errno)); 64*53607Selan } else { 65*53607Selan while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) { 66*53607Selan wcount = write(to_fd, buf, rcount); 67*53607Selan if (rcount != wcount || wcount == -1) { 68*53607Selan err("%s: %s", to.p_path, strerror(errno)); 69*53607Selan break; 70*53607Selan } 71*53607Selan } 72*53607Selan if (rcount < 0) 73*53607Selan err("%s: %s", from.p_path, strerror(errno)); 74*53607Selan } 75*53607Selan if (pflag) 76*53607Selan setfile(fs, to_fd); 77*53607Selan /* 78*53607Selan * If the source was setuid or setgid, lose the bits unless the 79*53607Selan * copy is owned by the same user and group. 80*53607Selan */ 81*53607Selan else if (fs->st_mode & (S_ISUID|S_ISGID) && fs->st_uid == myuid) 82*53607Selan if (fstat(to_fd, &to_stat)) 83*53607Selan err("%s: %s", to.p_path, strerror(errno)); 84*53607Selan #define RETAINBITS (S_ISUID|S_ISGID|S_ISVTX|S_IRWXU|S_IRWXG|S_IRWXO) 85*53607Selan else if (fs->st_gid == to_stat.st_gid && fchmod(to_fd, 86*53607Selan fs->st_mode & RETAINBITS & ~myumask)) 87*53607Selan err("%s: %s", to.p_path, strerror(errno)); 88*53607Selan (void)close(from_fd); 89*53607Selan if (close(to_fd)) 90*53607Selan err("%s: %s", to.p_path, strerror(errno)); 91*53607Selan } 92*53607Selan 93*53607Selan void 94*53607Selan copy_dir() 95*53607Selan { 96*53607Selan struct stat from_stat; 97*53607Selan struct dirent *dp, **dir_list; 98*53607Selan register int dir_cnt, i; 99*53607Selan char *old_from, *old_to; 100*53607Selan 101*53607Selan register FTS *ftsp; 102*53607Selan register FTSENT *p; 103*53607Selan 104*53607Selan dir_cnt = scandir(from.p_path, &dir_list, NULL, NULL); 105*53607Selan if (dir_cnt == -1) { 106*53607Selan (void)fprintf(stderr, "%s: can't read directory %s.\n", 107*53607Selan progname, from.p_path); 108*53607Selan exit_val = 1; 109*53607Selan } 110*53607Selan 111*53607Selan /* 112*53607Selan * Instead of handling directory entries in the order they appear 113*53607Selan * on disk, do non-directory files before directory files. 114*53607Selan * There are two reasons to do directories last. The first is 115*53607Selan * efficiency. Files tend to be in the same cylinder group as 116*53607Selan * their parent, whereas directories tend not to be. Copying files 117*53607Selan * all at once reduces seeking. Second, deeply nested tree's 118*53607Selan * could use up all the file descriptors if we didn't close one 119*53607Selan * directory before recursivly starting on the next. 120*53607Selan */ 121*53607Selan /* copy files */ 122*53607Selan for (i = 0; i < dir_cnt; ++i) { 123*53607Selan dp = dir_list[i]; 124*53607Selan if (dp->d_namlen <= 2 && dp->d_name[0] == '.' 125*53607Selan && (dp->d_name[1] == NULL || dp->d_name[1] == '.')) 126*53607Selan goto done; 127*53607Selan if (!(old_from = 128*53607Selan path_append(&from, dp->d_name, (int)dp->d_namlen))) 129*53607Selan goto done; 130*53607Selan 131*53607Selan if (statfcn(from.p_path, &from_stat) < 0) { 132*53607Selan err("%s: %s", dp->d_name, strerror(errno)); 133*53607Selan path_restore(&from, old_from); 134*53607Selan goto done; 135*53607Selan } 136*53607Selan if (S_ISDIR(from_stat.st_mode)) { 137*53607Selan path_restore(&from, old_from); 138*53607Selan continue; 139*53607Selan } 140*53607Selan if (old_to = path_append(&to, dp->d_name, (int)dp->d_namlen)) { 141*53607Selan copy(); 142*53607Selan path_restore(&to, old_to); 143*53607Selan } 144*53607Selan path_restore(&from, old_from); 145*53607Selan done: dir_list[i] = NULL; 146*53607Selan free(dp); 147*53607Selan } 148*53607Selan 149*53607Selan /* copy directories */ 150*53607Selan for (i = 0; i < dir_cnt; ++i) { 151*53607Selan dp = dir_list[i]; 152*53607Selan if (!dp) 153*53607Selan continue; 154*53607Selan if (!(old_from = 155*53607Selan path_append(&from, dp->d_name, (int)dp->d_namlen))) { 156*53607Selan free(dp); 157*53607Selan continue; 158*53607Selan } 159*53607Selan if (!(old_to = 160*53607Selan path_append(&to, dp->d_name, (int)dp->d_namlen))) { 161*53607Selan free(dp); 162*53607Selan path_restore(&from, old_from); 163*53607Selan continue; 164*53607Selan } 165*53607Selan copy(); 166*53607Selan free(dp); 167*53607Selan path_restore(&from, old_from); 168*53607Selan path_restore(&to, old_to); 169*53607Selan } 170*53607Selan free(dir_list); 171*53607Selan } 172*53607Selan 173*53607Selan void 174*53607Selan copy_link(exists) 175*53607Selan int exists; 176*53607Selan { 177*53607Selan int len; 178*53607Selan char link[MAXPATHLEN]; 179*53607Selan 180*53607Selan if ((len = readlink(from.p_path, link, sizeof(link))) == -1) { 181*53607Selan err("readlink: %s: %s", from.p_path, strerror(errno)); 182*53607Selan return; 183*53607Selan } 184*53607Selan link[len] = '\0'; 185*53607Selan if (exists && unlink(to.p_path)) { 186*53607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 187*53607Selan return; 188*53607Selan } 189*53607Selan if (symlink(link, to.p_path)) { 190*53607Selan err("symlink: %s: %s", link, strerror(errno)); 191*53607Selan return; 192*53607Selan } 193*53607Selan } 194*53607Selan 195*53607Selan void 196*53607Selan copy_fifo(from_stat, exists) 197*53607Selan struct stat *from_stat; 198*53607Selan int exists; 199*53607Selan { 200*53607Selan if (exists && unlink(to.p_path)) { 201*53607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 202*53607Selan return; 203*53607Selan } 204*53607Selan if (mkfifo(to.p_path, from_stat->st_mode)) { 205*53607Selan err("mkfifo: %s: %s", to.p_path, strerror(errno)); 206*53607Selan return; 207*53607Selan } 208*53607Selan if (pflag) 209*53607Selan setfile(from_stat, 0); 210*53607Selan } 211*53607Selan 212*53607Selan void 213*53607Selan copy_special(from_stat, exists) 214*53607Selan struct stat *from_stat; 215*53607Selan int exists; 216*53607Selan { 217*53607Selan if (exists && unlink(to.p_path)) { 218*53607Selan err("unlink: %s: %s", to.p_path, strerror(errno)); 219*53607Selan return; 220*53607Selan } 221*53607Selan if (mknod(to.p_path, from_stat->st_mode, from_stat->st_rdev)) { 222*53607Selan err("mknod: %s: %s", to.p_path, strerror(errno)); 223*53607Selan return; 224*53607Selan } 225*53607Selan if (pflag) 226*53607Selan setfile(from_stat, 0); 227*53607Selan } 228*53607Selan 229*53607Selan 230*53607Selan void 231*53607Selan setfile(fs, fd) 232*53607Selan register struct stat *fs; 233*53607Selan int fd; 234*53607Selan { 235*53607Selan static struct timeval tv[2]; 236*53607Selan 237*53607Selan fs->st_mode &= S_ISUID|S_ISGID|S_IRWXU|S_IRWXG|S_IRWXO; 238*53607Selan 239*53607Selan tv[0].tv_sec = fs->st_atime; 240*53607Selan tv[1].tv_sec = fs->st_mtime; 241*53607Selan if (utimes(to.p_path, tv)) 242*53607Selan err("utimes: %s: %s", to.p_path, strerror(errno)); 243*53607Selan /* 244*53607Selan * Changing the ownership probably won't succeed, unless we're root 245*53607Selan * or POSIX_CHOWN_RESTRICTED is not set. Set uid/gid before setting 246*53607Selan * the mode; current BSD behavior is to remove all setuid bits on 247*53607Selan * chown. If chown fails, lose setuid/setgid bits. 248*53607Selan */ 249*53607Selan if (fd ? fchown(fd, fs->st_uid, fs->st_gid) : 250*53607Selan chown(to.p_path, fs->st_uid, fs->st_gid)) { 251*53607Selan if (errno != EPERM) 252*53607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 253*53607Selan fs->st_mode &= ~(S_ISUID|S_ISGID); 254*53607Selan } 255*53607Selan if (fd ? fchmod(fd, fs->st_mode) : chmod(to.p_path, fs->st_mode)) 256*53607Selan err("chown: %s: %s", to.p_path, strerror(errno)); 257*53607Selan } 258*53607Selan 259*53607Selan void 260*53607Selan usage() 261*53607Selan { 262*53607Selan (void)fprintf(stderr, 263*53607Selan "usage: cp [-Rfhip] src target;\n cp [-Rfhip] src1 ... srcN directory\n"); 264*53607Selan exit(1); 265*53607Selan } 266*53607Selan 267*53607Selan #if __STDC__ 268*53607Selan #include <stdarg.h> 269*53607Selan #else 270*53607Selan #include <varargs.h> 271*53607Selan #endif 272*53607Selan 273*53607Selan void 274*53607Selan #if __STDC__ 275*53607Selan err(const char *fmt, ...) 276*53607Selan #else 277*53607Selan err(fmt, va_alist) 278*53607Selan char *fmt; 279*53607Selan va_dcl 280*53607Selan #endif 281*53607Selan { 282*53607Selan va_list ap; 283*53607Selan #if __STDC__ 284*53607Selan va_start(ap, fmt); 285*53607Selan #else 286*53607Selan va_start(ap); 287*53607Selan #endif 288*53607Selan (void)fprintf(stderr, "%s: ", progname); 289*53607Selan (void)vfprintf(stderr, fmt, ap); 290*53607Selan va_end(ap); 291*53607Selan (void)fprintf(stderr, "\n"); 292*53607Selan exit_val = 1; 293*53607Selan } 294