00001
00002
00003
00004 #define nil 0
00005 #include <sys/types.h>
00006 #include <sys/stat.h>
00007 #include <sys/wait.h>
00008 #include <stdio.h>
00009 #include <stdlib.h>
00010 #include <unistd.h>
00011 #include <fcntl.h>
00012 #include <string.h>
00013 #include <errno.h>
00014 #include <a.out.h>
00015 #include <limits.h>
00016 #include <pwd.h>
00017 #include <grp.h>
00018 #include <utime.h>
00019 #include <signal.h>
00020
00021
00022 char ZCAT[]= "#!/usr/bin/zexec /usr/bin/zcat\n";
00023 char GZCAT[]= "#!/usr/bin/zexec /usr/bin/gzcat\n";
00024
00025
00026 char *COMPRESS[]= { "compress", nil };
00027 char *GZIP[]= { "gzip", "-#", nil };
00028
00029 int excode= 0;
00030
00031 void report(char *label)
00032 {
00033 if (label == nil || label[0] == 0)
00034 fprintf(stderr, "install: %s\n", strerror(errno));
00035 else
00036 fprintf(stderr, "install: %s: %s\n", label, strerror(errno));
00037 excode= 1;
00038 }
00039
00040 void fatal(char *label)
00041 {
00042 report(label);
00043 exit(1);
00044 }
00045
00046 void *allocate(void *mem, size_t size)
00047
00048 {
00049 mem= mem == nil ? malloc(size) : realloc(mem, size);
00050
00051 if (mem == nil) fatal(nil);
00052 return mem;
00053 }
00054
00055 void deallocate(void *mem)
00056 {
00057 if (mem != nil) free(mem);
00058 }
00059
00060 int lflag= 0;
00061 int cflag= 0;
00062 int dflag= 0;
00063 int strip= 0;
00064 char **compress= nil;
00065 char *zcat= nil;
00066
00067 long stack= -1;
00068 int wordpow= 1;
00069
00070
00071 pid_t filter(int fd, char **command)
00072
00073 {
00074 pid_t pid;
00075 int pfd[2];
00076
00077 if (pipe(pfd) < 0) {
00078 report("pipe()");
00079 return -1;
00080 }
00081
00082 switch ((pid= fork())) {
00083 case -1:
00084 report("fork()");
00085 return -1;
00086 case 0:
00087
00088 dup2(pfd[0], 0);
00089 dup2(fd, 1);
00090 close(pfd[0]);
00091 close(pfd[1]);
00092 close(fd);
00093 signal(SIGPIPE, SIG_DFL);
00094 execvp(command[0], command);
00095 fatal(command[0]);
00096 }
00097
00098 dup2(pfd[1], fd);
00099 close(pfd[0]);
00100 close(pfd[1]);
00101 return pid;
00102 }
00103
00104 int mkdirp(char *dir, int mode, int owner, int group)
00105
00106 {
00107 int keep;
00108 char *sep, *pref;
00109
00110 sep= dir;
00111 while (*sep == '/') sep++;
00112
00113 if (*sep == 0) {
00114 errno= EINVAL;
00115 return -1;
00116 }
00117
00118 do {
00119 while (*sep != '/' && *sep != 0) sep++;
00120 pref= sep;
00121 while (*sep == '/') sep++;
00122
00123 keep= *pref; *pref= 0;
00124
00125 if (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0) continue;
00126
00127 if (mkdir(dir, mode) < 0) {
00128 if (errno != EEXIST || *sep == 0) {
00129
00130 return -1;
00131 }
00132 } else {
00133 if (chown(dir, owner, group) < 0 && errno != EPERM)
00134 return -1;
00135 }
00136 } while (*pref= keep, *sep != 0);
00137 return 0;
00138 }
00139
00140 void makedir(char *dir, int mode, int owner, int group)
00141
00142 {
00143 struct stat st;
00144
00145 if (stat(dir, &st) < 0) {
00146 if (errno != ENOENT) { report(dir); return; }
00147
00148
00149 if (mode == -1) mode= 0755;
00150 if (owner == -1) owner= getuid();
00151 if (group == -1) group= getgid();
00152
00153 if (mkdirp(dir, mode, owner, group) < 0) {
00154 report(dir); return;
00155 }
00156 } else {
00157
00158 if (mode == -1) mode= (st.st_mode & 07777) | 0555;
00159
00160 if ((st.st_mode & 07777) != mode) {
00161 if (chmod(dir, mode) < 0) { report(dir); return; }
00162 }
00163 if (owner == -1) owner= st.st_uid;
00164 if (group == -1) group= st.st_gid;
00165 if (st.st_uid != owner || st.st_gid != group) {
00166 if (chown(dir, owner, group) < 0 && errno != EPERM) {
00167 report(dir); return;
00168 }
00169
00170 (void) chmod(dir, mode);
00171 }
00172 }
00173 }
00174
00175 int setstack(struct exec *hdr)
00176
00177 {
00178 long total;
00179
00180 total= stack;
00181 while (wordpow > 0) {
00182 total *= hdr->a_cpu == A_I8086 ? 2 : 4;
00183 wordpow--;
00184 }
00185 total+= hdr->a_data + hdr->a_bss;
00186
00187 if (!(hdr->a_flags & A_SEP)) {
00188 total+= hdr->a_text;
00189 #ifdef A_PAL
00190 if (hdr->a_flags & A_PAL) total+= hdr->a_hdrlen;
00191 #endif
00192 }
00193 if (hdr->a_cpu == A_I8086 && total > 0x10000L)
00194 total= 0x10000L;
00195
00196 if (hdr->a_total != total) {
00197
00198 hdr->a_total= total;
00199
00200 return 1;
00201 }
00202 return 0;
00203 }
00204
00205 void copylink(char *source, char *dest, int mode, int owner, int group)
00206 {
00207 struct stat sst, dst;
00208 int sfd, dfd, n;
00209 int r, same= 0, change= 0, docopy= 1;
00210 char buf[4096];
00211 # define hdr ((struct exec *) buf)
00212 pid_t pid = 0;
00213 int status = 0;
00214
00215
00216
00217 if (stat(source, &sst) < 0) { report(source); return; }
00218
00219 if (mode == -1) {
00220 mode= sst.st_mode & 07777;
00221 if (!lflag || cflag) {
00222 mode|= 0444;
00223 if (mode & 0111) mode|= 0111;
00224 }
00225 }
00226 if (owner == -1) owner= sst.st_uid;
00227 if (group == -1) group= sst.st_gid;
00228
00229 if (!S_ISREG(sst.st_mode)) {
00230 fprintf(stderr, "install: %s is not a regular file\n", source);
00231 excode= 1;
00232 return;
00233 }
00234 r= stat(dest, &dst);
00235 if (r < 0) {
00236 if (errno != ENOENT) { report(dest); return; }
00237 } else {
00238 if (!S_ISREG(dst.st_mode)) {
00239 fprintf(stderr, "install: %s is not a regular file\n",
00240 dest);
00241 excode= 1;
00242 return;
00243 }
00244
00245
00246 if (sst.st_dev == dst.st_dev && sst.st_ino == dst.st_ino) {
00247 if (!lflag && cflag) {
00248 fprintf(stderr,
00249 "install: %s and %s are the same, can't copy\n",
00250 source, dest);
00251 excode= 1;
00252 return;
00253 }
00254 same= 1;
00255 }
00256 }
00257
00258 if (lflag && !same) {
00259
00260
00261 if (r >= 0 && unlink(dest) < 0) {
00262 report(dest); return;
00263 }
00264
00265 if (link(source, dest) >= 0) {
00266 docopy= 0;
00267 } else {
00268 if (!cflag || errno != EXDEV) {
00269 fprintf(stderr,
00270 "install: can't link %s to %s: %s\n",
00271 source, dest, strerror(errno));
00272 excode= 1;
00273 return;
00274 }
00275 }
00276 }
00277
00278 if (docopy && !same) {
00279
00280 long count= LONG_MAX;
00281 int first= 1;
00282
00283 if ((sfd= open(source, O_RDONLY)) < 0) {
00284 report(source); return;
00285 }
00286
00287
00288 dfd= open(dest, O_WRONLY|O_CREAT|O_TRUNC, mode | 0600);
00289 if (dfd < 0 && errno == EACCES) {
00290 (void) chmod(dest, mode | 0600);
00291 dfd= open(dest, O_WRONLY|O_TRUNC);
00292 }
00293 if (dfd < 0) {
00294 report(dest);
00295 close(sfd);
00296 return;
00297 }
00298
00299 pid= 0;
00300 while (count > 0 && (n= read(sfd, buf, sizeof(buf))) > 0) {
00301 if (first && n >= A_MINHDR && !BADMAG(*hdr)) {
00302 if (strip) {
00303 count= hdr->a_hdrlen
00304 + hdr->a_text + hdr->a_data;
00305 #ifdef A_NSYM
00306 hdr->a_flags &= ~A_NSYM;
00307 #endif
00308 hdr->a_syms= 0;
00309 }
00310 if (stack != -1 && setstack(hdr)) change= 1;
00311
00312 if (compress != nil) {
00313
00314 (void) write(dfd, zcat, strlen(zcat));
00315
00316
00317 if ((pid= filter(dfd, compress)) < 0) {
00318 close(sfd);
00319 close(dfd);
00320 return;
00321 }
00322 change= 1;
00323 }
00324 }
00325 if (count < n) n= count;
00326
00327 if (write(dfd, buf, n) < 0) {
00328 report(dest);
00329 close(sfd);
00330 close(dfd);
00331 if (pid != 0) (void) waitpid(pid, nil, 0);
00332 return;
00333 }
00334 count-= n;
00335 first= 0;
00336 }
00337 if (n < 0) report(source);
00338 close(sfd);
00339 close(dfd);
00340 if (pid != 0 && waitpid(pid, &status, 0) < 0 || status != 0) {
00341 excode= 1;
00342 return;
00343 }
00344 if (n < 0) return;
00345 } else {
00346 if (stack != -1) {
00347
00348
00349
00350 if ((dfd= open(dest, O_RDWR)) < 0) {
00351 report(dest);
00352 return;
00353 }
00354
00355 if ((n= read(dfd, buf, sizeof(*hdr))) < 0) {
00356 report(dest); return;
00357 }
00358
00359 if (n >= A_MINHDR && !BADMAG(*hdr) && setstack(hdr)) {
00360 if (lseek(dfd, (off_t) 0, SEEK_SET) == -1
00361 || write(dfd, buf, n) < 0
00362 ) {
00363 report(dest);
00364 close(dfd);
00365 return;
00366 }
00367 change= 1;
00368 }
00369 close(dfd);
00370 }
00371 }
00372
00373 if (stat(dest, &dst) < 0) { report(dest); return; }
00374
00375 if ((dst.st_mode & 07777) != mode) {
00376 if (chmod(dest, mode) < 0) { report(dest); return; }
00377 }
00378 if (dst.st_uid != owner || dst.st_gid != group) {
00379 if (chown(dest, owner, group) < 0 && errno != EPERM) {
00380 report(dest); return;
00381 }
00382
00383 (void) chmod(dest, mode);
00384 }
00385 if (!change) {
00386 struct utimbuf ubuf;
00387
00388 ubuf.actime= dst.st_atime;
00389 ubuf.modtime= sst.st_mtime;
00390
00391 if (utime(dest, &ubuf) < 0 && errno != EPERM) {
00392 report(dest); return;
00393 }
00394 }
00395 }
00396
00397 void usage(void)
00398 {
00399 fprintf(stderr, "\
00400 Usage:\n\
00401 install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] [file1] file2\n\
00402 install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] file ... dir\n\
00403 install -d [-o owner] [-g group] [-m mode] directory\n");
00404 exit(1);
00405 }
00406
00407 void main(int argc, char **argv)
00408 {
00409 int i= 1;
00410 int mode= -1;
00411 int owner= -1;
00412 int group= -1;
00413 int super = 0;
00414 #if NGROUPS_MAX > 0
00415 gid_t groups[NGROUPS_MAX];
00416 int ngroups;
00417 int g;
00418 #endif
00419
00420
00421 if (getgid() == 0) super = 1;
00422 #if NGROUPS_MAX > 0
00423 ngroups= getgroups(NGROUPS_MAX, groups);
00424 for (g= 0; g < ngroups; g++) if (groups[g] == 0) super= 1;
00425 #endif
00426 if (!super) {
00427 setgid(getgid());
00428 setuid(getuid());
00429 }
00430
00431
00432 signal(SIGPIPE, SIG_IGN);
00433
00434 while (i < argc && argv[i][0] == '-') {
00435 char *p= argv[i++]+1;
00436 char *end;
00437 unsigned long num;
00438 int wp;
00439 struct passwd *pw;
00440 struct group *gr;
00441
00442 if (strcmp(p, "-") == 0) break;
00443
00444 while (*p != 0) {
00445 switch (*p++) {
00446 case 'l': lflag= 1; break;
00447 case 'c': cflag= 1; break;
00448 case 's': strip= 1; break;
00449 case 'd': dflag= 1; break;
00450 case 'z':
00451 if (compress == nil) {
00452 compress= COMPRESS;
00453 zcat= ZCAT;
00454 }
00455 break;
00456 case 'o':
00457 if (*p == 0) {
00458 if (i == argc) usage();
00459 p= argv[i++];
00460 if (*p == 0) usage();
00461 }
00462 num= strtoul(p, &end, 10);
00463 if (*end == 0) {
00464 if ((uid_t) num != num) usage();
00465 owner= num;
00466 } else {
00467 if ((pw= getpwnam(p)) == nil) {
00468 fprintf(stderr,
00469 "install: %s: unknown user\n",
00470 p);
00471 exit(1);
00472 }
00473 owner= pw->pw_uid;
00474 }
00475 p= "";
00476 break;
00477 case 'g':
00478 if (*p == 0) {
00479 if (i == argc) usage();
00480 p= argv[i++];
00481 if (*p == 0) usage();
00482 }
00483 num= strtoul(p, &end, 10);
00484 if (*end == 0) {
00485 if ((gid_t) num != num) usage();
00486 group= num;
00487 } else {
00488 if ((gr= getgrnam(p)) == nil) {
00489 fprintf(stderr,
00490 "install: %s: unknown user\n",
00491 p);
00492 exit(1);
00493 }
00494 group= gr->gr_gid;
00495 }
00496 p= "";
00497 break;
00498 case 'm':
00499 if (*p == 0) {
00500 if (i == argc) usage();
00501 p= argv[i++];
00502 if (*p == 0) usage();
00503 }
00504 num= strtoul(p, &end, 010);
00505 if (*end != 0 || (num & 07777) != num) usage();
00506 mode= num;
00507 if ((mode & S_ISUID) && super && owner == -1) {
00508
00509 owner= 0;
00510 }
00511 if ((mode & S_ISGID) && super && group == -1) {
00512 group= 0;
00513 }
00514 p= "";
00515 break;
00516 case 'S':
00517 if (*p == 0) {
00518 if (i == argc) usage();
00519 p= argv[i++];
00520 if (*p == 0) usage();
00521 }
00522 stack= strtol(p, &end, 0);
00523 wp= 0;
00524 if (end == p || stack < 0) usage();
00525 p= end;
00526 while (*p != 0) {
00527 switch (*p++) {
00528 case 'm':
00529 case 'M': num= 1024 * 1024L; break;
00530 case 'k':
00531 case 'K': num= 1024; break;
00532 case 'w':
00533 case 'W': num= 4; wp++; break;
00534 case 'b':
00535 case 'B': num= 1; break;
00536 default: usage();
00537 }
00538 if (stack > LONG_MAX / num) usage();
00539 stack*= num;
00540 }
00541 wordpow= 0;
00542 while (wp > 0) { stack /= 4; wordpow++; wp--; }
00543 break;
00544 default:
00545 if ((unsigned) (p[-1] - '1') <= ('9' - '1')) {
00546 compress= GZIP;
00547 GZIP[1][1]= p[-1];
00548 zcat= GZCAT;
00549 break;
00550 }
00551 usage();
00552 }
00553 }
00554 }
00555
00556 if (dflag && (cflag || lflag || strip)) usage();
00557
00558
00559 umask(000);
00560
00561 if (dflag) {
00562
00563 if ((argc - i) != 1) usage();
00564
00565 makedir(argv[i], mode, owner, group);
00566 } else {
00567 struct stat st;
00568
00569 if ((argc - i) < 1) usage();
00570 if ((lflag || cflag) && (argc - i) == 1) usage();
00571
00572 if (stat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)) {
00573
00574 char *target= nil;
00575 char *base;
00576
00577 if ((argc - i) == 1) usage();
00578
00579 while (i < argc-1) {
00580 if ((base= strrchr(argv[i], '/')) == nil)
00581 base= argv[i];
00582 else
00583 base++;
00584 target= allocate(target, strlen(argv[argc-1])
00585 + 1 + strlen(base) + 1);
00586 strcpy(target, argv[argc-1]);
00587 strcat(target, "/");
00588 strcat(target, base);
00589
00590 copylink(argv[i++], target, mode, owner, group);
00591 }
00592 } else {
00593
00594
00595 copylink(argv[i], argv[argc-1], mode, owner, group);
00596 }
00597 }
00598 exit(excode);
00599 }