1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /* ***** BEGIN LICENSE BLOCK *****
3 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 : *
5 : * The contents of this file are subject to the Mozilla Public License Version
6 : * 1.1 (the "License"); you may not use this file except in compliance with
7 : * the License. You may obtain a copy of the License at
8 : * http://www.mozilla.org/MPL/
9 : *
10 : * Software distributed under the License is distributed on an "AS IS" basis,
11 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 : * for the specific language governing rights and limitations under the
13 : * License.
14 : *
15 : * The Original Code is the Netscape Portable Runtime (NSPR).
16 : *
17 : * The Initial Developer of the Original Code is
18 : * Netscape Communications Corporation.
19 : * Portions created by the Initial Developer are Copyright (C) 1998-2000
20 : * the Initial Developer. All Rights Reserved.
21 : *
22 : * Contributor(s):
23 : *
24 : * Alternatively, the contents of this file may be used under the terms of
25 : * either the GNU General Public License Version 2 or later (the "GPL"), or
26 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 : * in which case the provisions of the GPL or the LGPL are applicable instead
28 : * of those above. If you wish to allow use of your version of this file only
29 : * under the terms of either the GPL or the LGPL, and not to allow others to
30 : * use your version of this file under the terms of the MPL, indicate your
31 : * decision by deleting the provisions above and replace them with the notice
32 : * and other provisions required by the GPL or the LGPL. If you do not delete
33 : * the provisions above, a recipient may use your version of this file under
34 : * the terms of any one of the MPL, the GPL or the LGPL.
35 : *
36 : * ***** END LICENSE BLOCK ***** */
37 :
38 : /*
39 : ** Netscape portable install command.
40 : **
41 : ** Brendan Eich, 7/20/95
42 : */
43 : #include <stdio.h> /* OSF/1 requires this before grp.h, so put it first */
44 : #include <assert.h>
45 : #include <fcntl.h>
46 : #include <grp.h>
47 : #include <pwd.h>
48 : #include <stdlib.h>
49 : #include <string.h>
50 : #include <unistd.h>
51 : #include <utime.h>
52 : #include <sys/types.h>
53 : #include <sys/stat.h>
54 : #include <dirent.h>
55 : #include <errno.h>
56 : #include <stdarg.h>
57 : #ifdef USE_REENTRANT_LIBC
58 : #include "libc_r.h"
59 : #endif /* USE_REENTRANT_LIBC */
60 :
61 : #include "pathsub.h"
62 :
63 : #define HAVE_FCHMOD
64 :
65 : #if defined(BEOS)
66 : #undef HAVE_FCHMOD
67 : #endif
68 :
69 : /*
70 : * Does getcwd() take NULL as the first argument and malloc
71 : * the result buffer?
72 : */
73 : #if !defined(DARWIN) && !defined(NEXTSTEP)
74 : #define GETCWD_CAN_MALLOC
75 : #endif
76 :
77 : #ifdef NEXTSTEP
78 : #include <bsd/libc.h>
79 :
80 : /*
81 : ** balazs.pataki@sztaki.hu: The getcwd is broken in NEXTSTEP (returns 0),
82 : ** when called on a mounted fs. Did anyone notice this? Here's an ugly
83 : ** workaround ...
84 : */
85 : #define getcwd(b,s) my_getcwd(b,s)
86 :
87 : static char *
88 : my_getcwd (char *buf, size_t size)
89 : {
90 : FILE *pwd = popen("pwd", "r");
91 : char *result = fgets(buf, size, pwd);
92 :
93 : if (result) {
94 : buf[strlen(buf)-1] = '\0';
95 : }
96 : pclose (pwd);
97 : return buf;
98 : }
99 : #endif /* NEXTSTEP */
100 :
101 : #if defined(LINUX) || defined(__GNU__) || defined(__GLIBC__)
102 : #include <getopt.h>
103 : #endif
104 :
105 : #if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC) || defined(NEXTSTEP)
106 : #if !defined(S_ISLNK) && defined(S_IFLNK)
107 : #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK)
108 : #endif
109 : #endif
110 :
111 : #if defined(SNI)
112 : extern int fchmod(int fildes, mode_t mode);
113 : #endif
114 :
115 : #ifdef QNX
116 : #define d_ino d_stat.st_ino
117 : #endif
118 :
119 : static void
120 0 : usage(void)
121 : {
122 0 : fprintf(stderr,
123 : "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
124 : " %*s [-DdltR] file [file ...] directory\n",
125 0 : program, (int)strlen(program), "");
126 0 : exit(2);
127 : }
128 :
129 : static int
130 9 : mkdirs(char *path, mode_t mode)
131 : {
132 : char *cp;
133 : struct stat sb;
134 : int res;
135 :
136 18 : while (*path == '/' && path[1] == '/')
137 0 : path++;
138 9 : for (cp = strrchr(path, '/'); cp && cp != path && cp[-1] == '/'; cp--)
139 : ;
140 9 : if (cp && cp != path) {
141 9 : *cp = '\0';
142 10 : if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
143 1 : mkdirs(path, mode) < 0) {
144 0 : return -1;
145 : }
146 9 : *cp = '/';
147 : }
148 9 : res = mkdir(path, mode);
149 9 : if ((res != 0) && (errno == EEXIST))
150 0 : return 0;
151 : else
152 9 : return res;
153 : }
154 :
155 : static uid_t
156 0 : touid(char *owner)
157 : {
158 : struct passwd *pw;
159 : uid_t uid;
160 : char *cp;
161 :
162 0 : pw = getpwnam(owner);
163 0 : if (pw)
164 0 : return pw->pw_uid;
165 0 : uid = strtol(owner, &cp, 0);
166 0 : if (uid == 0 && cp == owner)
167 0 : fail("cannot find uid for %s", owner);
168 0 : return uid;
169 : }
170 :
171 : static gid_t
172 0 : togid(char *group)
173 : {
174 : struct group *gr;
175 : gid_t gid;
176 : char *cp;
177 :
178 0 : gr = getgrnam(group);
179 0 : if (gr)
180 0 : return gr->gr_gid;
181 0 : gid = strtol(group, &cp, 0);
182 0 : if (gid == 0 && cp == group)
183 0 : fail("cannot find gid for %s", group);
184 0 : return gid;
185 : }
186 :
187 : int
188 28 : main(int argc, char **argv)
189 : {
190 : int onlydir, dodir, dolink, dorelsymlink, dotimes, opt, len, lplen, tdlen, bnlen, exists, fromfd, tofd, cc, wc;
191 28 : mode_t mode = 0755;
192 : char *linkprefix, *owner, *group, *cp, *cwd, *todir, *toname, *name, *base, *linkname, *bp, buf[BUFSIZ];
193 : uid_t uid;
194 : gid_t gid;
195 : struct stat sb, tosb;
196 : struct utimbuf utb;
197 :
198 28 : program = argv[0];
199 28 : cwd = linkname = linkprefix = owner = group = 0;
200 28 : onlydir = dodir = dolink = dorelsymlink = dotimes = lplen = 0;
201 :
202 110 : while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
203 54 : switch (opt) {
204 : case 'C':
205 0 : cwd = optarg;
206 0 : break;
207 : case 'D':
208 2 : onlydir = 1;
209 2 : break;
210 : case 'd':
211 0 : dodir = 1;
212 0 : break;
213 : case 'l':
214 0 : dolink = 1;
215 0 : break;
216 : case 'L':
217 0 : linkprefix = optarg;
218 0 : lplen = strlen(linkprefix);
219 0 : dolink = 1;
220 0 : break;
221 : case 'R':
222 13 : dolink = dorelsymlink = 1;
223 13 : break;
224 : case 'm':
225 26 : mode = strtoul(optarg, &cp, 8);
226 26 : if (mode == 0 && cp == optarg)
227 0 : usage();
228 26 : break;
229 : case 'o':
230 0 : owner = optarg;
231 0 : break;
232 : case 'g':
233 0 : group = optarg;
234 0 : break;
235 : case 't':
236 13 : dotimes = 1;
237 13 : break;
238 : default:
239 0 : usage();
240 : }
241 : }
242 :
243 28 : argc -= optind;
244 28 : argv += optind;
245 28 : if (argc < 2 - onlydir)
246 0 : usage();
247 :
248 28 : todir = argv[argc-1];
249 36 : if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
250 8 : mkdirs(todir, 0777) < 0) {
251 0 : fail("cannot make directory %s", todir);
252 : }
253 28 : if (onlydir)
254 2 : return 0;
255 :
256 26 : if (!cwd) {
257 : #ifdef GETCWD_CAN_MALLOC
258 26 : cwd = getcwd(0, PATH_MAX);
259 : #else
260 : cwd = malloc(PATH_MAX + 1);
261 : cwd = getcwd(cwd, PATH_MAX);
262 : #endif
263 : }
264 26 : xchdir(todir);
265 : #ifdef GETCWD_CAN_MALLOC
266 26 : todir = getcwd(0, PATH_MAX);
267 : #else
268 : todir = malloc(PATH_MAX + 1);
269 : todir = getcwd(todir, PATH_MAX);
270 : #endif
271 26 : tdlen = strlen(todir);
272 26 : xchdir(cwd);
273 26 : tdlen = strlen(todir);
274 :
275 26 : uid = owner ? touid(owner) : -1;
276 26 : gid = group ? togid(group) : -1;
277 :
278 245 : while (--argc > 0) {
279 193 : name = *argv++;
280 193 : len = strlen(name);
281 193 : base = xbasename(name);
282 193 : bnlen = strlen(base);
283 193 : toname = (char*)xmalloc(tdlen + 1 + bnlen + 1);
284 193 : sprintf(toname, "%s/%s", todir, base);
285 193 : exists = (lstat(toname, &tosb) == 0);
286 :
287 193 : if (dodir) {
288 : /* -d means create a directory, always */
289 0 : if (exists && !S_ISDIR(tosb.st_mode)) {
290 0 : (void) unlink(toname);
291 0 : exists = 0;
292 : }
293 0 : if (!exists && mkdir(toname, mode) < 0)
294 0 : fail("cannot make directory %s", toname);
295 0 : if ((owner || group) && chown(toname, uid, gid) < 0)
296 0 : fail("cannot change owner of %s", toname);
297 193 : } else if (dolink) {
298 96 : if (*name == '/') {
299 : /* source is absolute pathname, link to it directly */
300 87 : linkname = 0;
301 : } else {
302 9 : if (linkprefix) {
303 : /* -L implies -l and prefixes names with a $cwd arg. */
304 0 : len += lplen + 1;
305 0 : linkname = (char*)xmalloc(len + 1);
306 0 : sprintf(linkname, "%s/%s", linkprefix, name);
307 9 : } else if (dorelsymlink) {
308 : /* Symlink the relative path from todir to source name. */
309 9 : linkname = (char*)xmalloc(PATH_MAX);
310 :
311 9 : if (*todir == '/') {
312 : /* todir is absolute: skip over common prefix. */
313 9 : lplen = relatepaths(todir, cwd, linkname);
314 9 : strcpy(linkname + lplen, name);
315 : } else {
316 : /* todir is named by a relative path: reverse it. */
317 0 : reversepath(todir, name, len, linkname);
318 0 : xchdir(cwd);
319 : }
320 :
321 9 : len = strlen(linkname);
322 : }
323 9 : name = linkname;
324 : }
325 :
326 : /* Check for a pre-existing symlink with identical content. */
327 96 : if (exists &&
328 0 : (!S_ISLNK(tosb.st_mode) ||
329 0 : readlink(toname, buf, sizeof buf) != len ||
330 0 : strncmp(buf, name, len) != 0)) {
331 0 : (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
332 0 : exists = 0;
333 : }
334 96 : if (!exists && symlink(name, toname) < 0)
335 0 : fail("cannot make symbolic link %s", toname);
336 : #ifdef HAVE_LCHOWN
337 96 : if ((owner || group) && lchown(toname, uid, gid) < 0)
338 0 : fail("cannot change owner of %s", toname);
339 : #endif
340 :
341 96 : if (linkname) {
342 9 : free(linkname);
343 9 : linkname = 0;
344 : }
345 : } else {
346 : /* Copy from name to toname, which might be the same file. */
347 97 : fromfd = open(name, O_RDONLY);
348 97 : if (fromfd < 0 || fstat(fromfd, &sb) < 0)
349 0 : fail("cannot access %s", name);
350 97 : if (exists && (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0))
351 0 : (void) (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
352 97 : tofd = open(toname, O_CREAT | O_WRONLY, 0666);
353 97 : if (tofd < 0)
354 0 : fail("cannot create %s", toname);
355 :
356 97 : bp = buf;
357 714 : while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
358 1040 : while ((wc = write(tofd, bp, cc)) > 0) {
359 520 : if ((cc -= wc) == 0)
360 520 : break;
361 0 : bp += wc;
362 : }
363 520 : if (wc < 0)
364 0 : fail("cannot write to %s", toname);
365 : }
366 97 : if (cc < 0)
367 0 : fail("cannot read from %s", name);
368 :
369 97 : if (ftruncate(tofd, sb.st_size) < 0)
370 0 : fail("cannot truncate %s", toname);
371 97 : if (dotimes) {
372 97 : utb.actime = sb.st_atime;
373 97 : utb.modtime = sb.st_mtime;
374 97 : if (utime(toname, &utb) < 0)
375 0 : fail("cannot set times of %s", toname);
376 : }
377 : #ifdef HAVE_FCHMOD
378 97 : if (fchmod(tofd, mode) < 0)
379 : #else
380 : if (chmod(toname, mode) < 0)
381 : #endif
382 0 : fail("cannot change mode of %s", toname);
383 97 : if ((owner || group) && fchown(tofd, uid, gid) < 0)
384 0 : fail("cannot change owner of %s", toname);
385 :
386 : /* Must check for delayed (NFS) write errors on close. */
387 97 : if (close(tofd) < 0)
388 0 : fail("cannot write to %s", toname);
389 97 : close(fromfd);
390 : }
391 :
392 193 : free(toname);
393 : }
394 :
395 26 : free(cwd);
396 26 : free(todir);
397 26 : return 0;
398 : }
399 :
400 : /*
401 : ** Pathname subroutines.
402 : **
403 : ** Brendan Eich, 8/29/95
404 : */
405 :
406 : char *program;
407 :
408 : void
409 0 : fail(char *format, ...)
410 : {
411 : int error;
412 : va_list ap;
413 :
414 : #ifdef USE_REENTRANT_LIBC
415 : R_STRERROR_INIT_R();
416 : #endif
417 :
418 0 : error = errno;
419 0 : fprintf(stderr, "%s: ", program);
420 0 : va_start(ap, format);
421 0 : vfprintf(stderr, format, ap);
422 0 : va_end(ap);
423 0 : if (error)
424 :
425 : #ifdef USE_REENTRANT_LIBC
426 : R_STRERROR_R(errno);
427 : fprintf(stderr, ": %s", r_strerror_r);
428 : #else
429 0 : fprintf(stderr, ": %s", strerror(errno));
430 : #endif
431 :
432 0 : putc('\n', stderr);
433 0 : exit(1);
434 : }
435 :
436 : char *
437 66 : getcomponent(char *path, char *name)
438 : {
439 66 : if (*path == '\0')
440 18 : return 0;
441 48 : if (*path == '/') {
442 0 : *name++ = '/';
443 : } else {
444 : do {
445 186 : *name++ = *path++;
446 186 : } while (*path != '/' && *path != '\0');
447 : }
448 48 : *name = '\0';
449 126 : while (*path == '/')
450 30 : path++;
451 48 : return path;
452 : }
453 :
454 : #ifdef UNIXWARE_READDIR_BUFFER_TOO_SMALL
455 : /* Sigh. The static buffer in Unixware's readdir is too small. */
456 : struct dirent * readdir(DIR *d)
457 : {
458 : static struct dirent *buf = NULL;
459 : #define MAX_PATH_LEN 1024
460 :
461 :
462 : if(buf == NULL)
463 : buf = (struct dirent *) malloc(sizeof(struct dirent) + MAX_PATH_LEN)
464 : ;
465 : return(readdir_r(d, buf));
466 : }
467 : #endif
468 :
469 : char *
470 0 : ino2name(ino_t ino, char *dir)
471 : {
472 : DIR *dp;
473 : struct dirent *ep;
474 : char *name;
475 :
476 0 : dp = opendir("..");
477 0 : if (!dp)
478 0 : fail("cannot read parent directory");
479 : for (;;) {
480 0 : if (!(ep = readdir(dp)))
481 0 : fail("cannot find current directory");
482 0 : if (ep->d_ino == ino)
483 : break;
484 0 : }
485 0 : name = xstrdup(ep->d_name);
486 0 : closedir(dp);
487 0 : return name;
488 : }
489 :
490 : void *
491 202 : xmalloc(size_t size)
492 : {
493 202 : void *p = malloc(size);
494 202 : if (!p)
495 0 : fail("cannot allocate %u bytes", size);
496 202 : return p;
497 : }
498 :
499 : char *
500 0 : xstrdup(char *s)
501 : {
502 0 : return strcpy((char*)xmalloc(strlen(s) + 1), s);
503 : }
504 :
505 : char *
506 193 : xbasename(char *path)
507 : {
508 : char *cp;
509 :
510 386 : while ((cp = strrchr(path, '/')) && cp[1] == '\0')
511 0 : *cp = '\0';
512 193 : if (!cp) return path;
513 192 : return cp + 1;
514 : }
515 :
516 : void
517 52 : xchdir(char *dir)
518 : {
519 52 : if (chdir(dir) < 0)
520 0 : fail("cannot change directory to %s", dir);
521 52 : }
522 :
523 : int
524 9 : relatepaths(char *from, char *to, char *outpath)
525 : {
526 : char *cp, *cp2;
527 : int len;
528 : char buf[NAME_MAX];
529 :
530 9 : assert(*from == '/' && *to == '/');
531 468 : for (cp = to, cp2 = from; *cp == *cp2; cp++, cp2++)
532 459 : if (*cp == '\0')
533 0 : break;
534 18 : while (cp[-1] != '/')
535 0 : cp--, cp2--;
536 9 : if (cp - 1 == to) {
537 : /* closest common ancestor is /, so use full pathname */
538 0 : len = strlen(strcpy(outpath, to));
539 0 : if (outpath[len] != '/') {
540 0 : outpath[len++] = '/';
541 0 : outpath[len] = '\0';
542 : }
543 : } else {
544 9 : len = 0;
545 36 : while ((cp2 = getcomponent(cp2, buf)) != 0) {
546 18 : strcpy(outpath + len, "../");
547 18 : len += 3;
548 : }
549 48 : while ((cp = getcomponent(cp, buf)) != 0) {
550 30 : sprintf(outpath + len, "%s/", buf);
551 30 : len += strlen(outpath + len);
552 : }
553 : }
554 9 : return len;
555 : }
556 :
557 : void
558 0 : reversepath(char *inpath, char *name, int len, char *outpath)
559 : {
560 : char *cp, *cp2;
561 : char buf[NAME_MAX];
562 : struct stat sb;
563 :
564 0 : cp = strcpy(outpath + PATH_MAX - (len + 1), name);
565 0 : cp2 = inpath;
566 0 : while ((cp2 = getcomponent(cp2, buf)) != 0) {
567 0 : if (strcmp(buf, ".") == 0)
568 0 : continue;
569 0 : if (strcmp(buf, "..") == 0) {
570 0 : if (stat(".", &sb) < 0)
571 0 : fail("cannot stat current directory");
572 0 : name = ino2name(sb.st_ino, "..");
573 0 : len = strlen(name);
574 0 : cp -= len + 1;
575 0 : strcpy(cp, name);
576 0 : cp[len] = '/';
577 0 : free(name);
578 0 : xchdir("..");
579 : } else {
580 0 : cp -= 3;
581 0 : strncpy(cp, "../", 3);
582 0 : xchdir(buf);
583 : }
584 : }
585 0 : strcpy(outpath, cp);
586 0 : }
|