cp.c (3779B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <dirent.h> 3 #include <errno.h> 4 #include <fcntl.h> 5 #include <limits.h> 6 #include <stdio.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <sys/stat.h> 10 #include <sys/types.h> 11 #include <unistd.h> 12 #include <utime.h> 13 14 #include "../fs.h" 15 #include "../util.h" 16 17 int cp_aflag = 0; 18 int cp_fflag = 0; 19 int cp_pflag = 0; 20 int cp_rflag = 0; 21 int cp_vflag = 0; 22 int cp_status = 0; 23 int cp_follow = 'L'; 24 25 int 26 cp(const char *s1, const char *s2, int depth) 27 { 28 DIR *dp; 29 int f1, f2; 30 struct dirent *d; 31 struct stat st; 32 struct timespec times[2]; 33 ssize_t r; 34 int (*statf)(const char *, struct stat *); 35 char target[PATH_MAX], ns1[PATH_MAX], ns2[PATH_MAX], *statf_name; 36 37 if (cp_follow == 'P' || (cp_follow == 'H' && depth)) { 38 statf_name = "lstat"; 39 statf = lstat; 40 } else { 41 statf_name = "stat"; 42 statf = stat; 43 } 44 45 if (statf(s1, &st) < 0) { 46 weprintf("%s %s:", statf_name, s1); 47 cp_status = 1; 48 return 0; 49 } 50 51 if (cp_vflag) 52 printf("%s -> %s\n", s1, s2); 53 54 if (S_ISLNK(st.st_mode)) { 55 if ((r = readlink(s1, target, sizeof(target) - 1)) >= 0) { 56 target[r] = '\0'; 57 if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) { 58 weprintf("unlink %s:", s2); 59 cp_status = 1; 60 return 0; 61 } else if (symlink(target, s2) < 0) { 62 weprintf("symlink %s -> %s:", s2, target); 63 cp_status = 1; 64 return 0; 65 } 66 } 67 } else if (S_ISDIR(st.st_mode)) { 68 if (!cp_rflag) { 69 weprintf("%s is a directory\n", s1); 70 cp_status = 1; 71 return 0; 72 } 73 if (!(dp = opendir(s1))) { 74 weprintf("opendir %s:", s1); 75 cp_status = 1; 76 return 0; 77 } 78 if (mkdir(s2, st.st_mode) < 0 && errno != EEXIST) { 79 weprintf("mkdir %s:", s2); 80 cp_status = 1; 81 closedir(dp); 82 return 0; 83 } 84 85 while ((d = readdir(dp))) { 86 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 87 continue; 88 89 estrlcpy(ns1, s1, sizeof(ns1)); 90 if (s1[strlen(s1) - 1] != '/') 91 estrlcat(ns1, "/", sizeof(ns1)); 92 estrlcat(ns1, d->d_name, sizeof(ns1)); 93 94 estrlcpy(ns2, s2, sizeof(ns2)); 95 if (s2[strlen(s2) - 1] != '/') 96 estrlcat(ns2, "/", sizeof(ns2)); 97 estrlcat(ns2, d->d_name, sizeof(ns2)); 98 99 fnck(ns1, ns2, cp, depth + 1); 100 } 101 102 closedir(dp); 103 } else if (cp_aflag && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || 104 S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode))) { 105 if (cp_fflag && unlink(s2) < 0 && errno != ENOENT) { 106 weprintf("unlink %s:", s2); 107 cp_status = 1; 108 return 0; 109 } else if (mknod(s2, st.st_mode, st.st_rdev) < 0) { 110 weprintf("mknod %s:", s2); 111 cp_status = 1; 112 return 0; 113 } 114 } else { 115 if ((f1 = open(s1, O_RDONLY)) < 0) { 116 weprintf("open %s:", s1); 117 cp_status = 1; 118 return 0; 119 } 120 if ((f2 = creat(s2, st.st_mode)) < 0 && cp_fflag) { 121 if (unlink(s2) < 0 && errno != ENOENT) { 122 weprintf("unlink %s:", s2); 123 cp_status = 1; 124 close(f1); 125 return 0; 126 } 127 f2 = creat(s2, st.st_mode); 128 } 129 if (f2 < 0) { 130 weprintf("creat %s:", s2); 131 cp_status = 1; 132 close(f1); 133 return 0; 134 } 135 if (concat(f1, s1, f2, s2) < 0) { 136 cp_status = 1; 137 close(f1); 138 close(f2); 139 return 0; 140 } 141 142 close(f1); 143 close(f2); 144 } 145 146 if (cp_aflag || cp_pflag) { 147 /* atime and mtime */ 148 times[0] = st.st_atim; 149 times[1] = st.st_mtim; 150 if (utimensat(AT_FDCWD, s2, times, AT_SYMLINK_NOFOLLOW) < 0) { 151 weprintf("utimensat %s:", s2); 152 cp_status = 1; 153 } 154 155 /* owner and mode */ 156 if (!S_ISLNK(st.st_mode)) { 157 if (chown(s2, st.st_uid, st.st_gid) < 0) { 158 weprintf("chown %s:", s2); 159 cp_status = 1; 160 st.st_mode &= ~(S_ISUID | S_ISGID); 161 } 162 if (chmod(s2, st.st_mode) < 0) { 163 weprintf("chmod %s:", s2); 164 cp_status = 1; 165 } 166 } else { 167 if (lchown(s2, st.st_uid, st.st_gid) < 0) { 168 weprintf("lchown %s:", s2); 169 cp_status = 1; 170 return 0; 171 } 172 } 173 } 174 175 return 0; 176 }