xinstall.c (4859B)
1 /* See LICENSE file for copyright and license details. */ 2 #include <grp.h> 3 #include <pwd.h> 4 #include <errno.h> 5 #include <fcntl.h> 6 #include <unistd.h> 7 #include <stdlib.h> 8 #include <string.h> 9 #include <dirent.h> 10 #include <sys/stat.h> 11 #include <sys/wait.h> 12 13 #include "util.h" 14 15 static int Dflag = 0; 16 static gid_t group; 17 static uid_t owner; 18 static mode_t mode = 0755; 19 20 static void 21 make_dir(char *dir, int was_missing) 22 { 23 if (!mkdir(dir, was_missing ? 0755 : mode)) { 24 if (!was_missing && (lchown(dir, owner, group) < 0)) 25 eprintf("lchmod %s:", dir); 26 } else if (errno != EEXIST) { 27 eprintf("mkdir %s:", dir); 28 } 29 } 30 31 static void 32 make_dirs(char *dir, int was_missing) 33 { 34 char *p; 35 for (p = strchr(dir + (dir[0] == '/'), '/'); p; p = strchr(p + 1, '/')) { 36 *p = '\0'; 37 make_dir(dir, was_missing); 38 *p = '/'; 39 } 40 make_dir(dir, was_missing); 41 } 42 43 static int 44 install(const char *s1, const char *s2, int depth) 45 { 46 DIR *dp; 47 int f1, f2; 48 struct dirent *d; 49 struct stat st; 50 ssize_t r; 51 char target[PATH_MAX], ns1[PATH_MAX], ns2[PATH_MAX]; 52 53 if (stat(s1, &st) < 0) 54 eprintf("stat %s:", s1); 55 56 if (S_ISLNK(st.st_mode)) { 57 if ((r = readlink(s1, target, sizeof(target) - 1)) >= 0) { 58 target[r] = '\0'; 59 if (unlink(s2) < 0 && errno != ENOENT) 60 eprintf("unlink %s:", s2); 61 else if (symlink(target, s2) < 0) 62 eprintf("symlink %s -> %s:", s2, target); 63 } 64 } else if (S_ISDIR(st.st_mode)) { 65 if (!(dp = opendir(s1))) 66 eprintf("opendir %s:", s1); 67 if (mkdir(s2, mode | 0111) < 0 && errno != EEXIST) 68 eprintf("mkdir %s:", s2); 69 70 while ((d = readdir(dp))) { 71 if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 72 continue; 73 74 estrlcpy(ns1, s1, sizeof(ns1)); 75 if (s1[strlen(s1) - 1] != '/') 76 estrlcat(ns1, "/", sizeof(ns1)); 77 estrlcat(ns1, d->d_name, sizeof(ns1)); 78 79 estrlcpy(ns2, s2, sizeof(ns2)); 80 if (s2[strlen(s2) - 1] != '/') 81 estrlcat(ns2, "/", sizeof(ns2)); 82 estrlcat(ns2, d->d_name, sizeof(ns2)); 83 84 fnck(ns1, ns2, install, depth + 1); 85 } 86 87 closedir(dp); 88 } else if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode) || 89 S_ISSOCK(st.st_mode) || S_ISFIFO(st.st_mode)) { 90 if (unlink(s2) < 0 && errno != ENOENT) 91 eprintf("unlink %s:", s2); 92 else if (mknod(s2, (st.st_mode & ~07777) | mode, st.st_rdev) < 0) 93 eprintf("mknod %s:", s2); 94 } else { 95 if ((f1 = open(s1, O_RDONLY)) < 0) 96 eprintf("open %s:", s1); 97 if ((f2 = creat(s2, 0600)) < 0) { 98 if (unlink(s2) < 0 && errno != ENOENT) 99 eprintf("unlink %s:", s2); 100 if ((f2 = creat(s2, 0600)) < 0) 101 eprintf("creat %s:", s2); 102 } 103 if (concat(f1, s1, f2, s2) < 0) 104 exit(1); 105 106 if (fchmod(f2, mode) < 0) 107 eprintf("fchmod %s:", s2); 108 109 close(f1); 110 close(f2); 111 } 112 113 if (lchown(s2, owner, group) < 0) 114 eprintf("lchown %s:", s2); 115 116 return 0; 117 } 118 119 static void 120 usage(void) 121 { 122 eprintf("usage: %s [-g group] [-o owner] [-m mode] (-d dir ... | [-D] (-t dest source ... | source ... dest))\n", argv0); 123 } 124 125 int 126 main(int argc, char *argv[]) 127 { 128 int dflag = 0; 129 char *gflag = 0; 130 char *oflag = 0; 131 char *mflag = 0; 132 char *tflag = 0; 133 struct group *gr; 134 struct passwd *pw; 135 struct stat st; 136 char *p; 137 138 ARGBEGIN { 139 case 'c': 140 /* no-op for compatibility */ 141 break; 142 case 'd': 143 dflag = 1; 144 break; 145 case 'D': 146 Dflag = 1; 147 break; 148 case 's': 149 /* no-op for compatibility */ 150 break; 151 case 'g': 152 gflag = EARGF(usage()); 153 break; 154 case 'o': 155 oflag = EARGF(usage()); 156 break; 157 case 'm': 158 mflag = EARGF(usage()); 159 break; 160 case 't': 161 tflag = EARGF(usage()); 162 break; 163 default: 164 usage(); 165 } ARGEND 166 167 if (argc < 1 + (!tflag & !dflag) || dflag & (Dflag | !!tflag)) 168 usage(); 169 170 if (gflag) { 171 errno = 0; 172 gr = getgrnam(gflag); 173 if (gr) { 174 group = gr->gr_gid; 175 } else { 176 if (errno) 177 eprintf("getgrnam %s:", gflag); 178 group = estrtonum(gflag, 0, UINT_MAX); 179 } 180 } else { 181 group = getgid(); 182 } 183 184 if (oflag) { 185 errno = 0; 186 pw = getpwnam(oflag); 187 if (pw) { 188 owner = pw->pw_uid; 189 } else { 190 if (errno) 191 eprintf("getpwnam %s:", oflag); 192 owner = estrtonum(oflag, 0, UINT_MAX); 193 } 194 } else { 195 owner = getuid(); 196 } 197 198 if (mflag) 199 mode = parsemode(mflag, mode, 0); 200 201 if (tflag) { 202 argv = memmove(argv - 1, argv, argc * sizeof(*argv)); 203 argv[argc++] = tflag; 204 } 205 if (tflag || argc > 2) { 206 if (stat(argv[argc - 1], &st) < 0) { 207 if ((errno == ENOENT) && Dflag) { 208 make_dirs(argv[argc - 1], 1); 209 } else { 210 eprintf("stat %s:", argv[argc - 1]); 211 } 212 } else if (!S_ISDIR(st.st_mode)) { 213 eprintf("%s: not a directory\n", argv[argc - 1]); 214 } 215 } 216 217 if (dflag) { 218 for (; *argv; argc--, argv++) 219 make_dirs(*argv, 0); 220 } else { 221 if (stat(argv[argc - 1], &st) < 0) { 222 if (errno != ENOENT) 223 eprintf("stat %s:", argv[argc - 1]); 224 if (tflag || Dflag || argc > 2) { 225 if ((p = strrchr(argv[argc - 1], '/')) != NULL) { 226 *p = '\0'; 227 make_dirs(argv[argc - 1], 1); 228 *p = '/'; 229 } 230 } 231 } 232 enmasse(argc, argv, install); 233 } 234 235 return 0; 236 }