tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

nsinstall.c (9608B)


      1 /* This Source Code Form is subject to the terms of the Mozilla Public
      2 * License, v. 2.0. If a copy of the MPL was not distributed with this
      3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      4 
      5 /*
      6 ** Netscape portable install command.
      7 */
      8 #include <stdio.h>  /* OSF/1 requires this before grp.h, so put it first */
      9 #include <assert.h>
     10 #include <fcntl.h>
     11 #include <string.h>
     12 #if defined(_WINDOWS)
     13 #include <windows.h>
     14 typedef unsigned int mode_t;
     15 #else
     16 #include <grp.h>
     17 #include <pwd.h>
     18 #include <errno.h>
     19 #include <stdlib.h>
     20 #include <unistd.h>
     21 #include <utime.h>
     22 #endif
     23 #include <sys/types.h>
     24 #include <sys/stat.h>
     25 #include "pathsub.h"
     26 
     27 #define HAVE_LCHOWN
     28 
     29 #if defined(AIX) || defined(HPUX) || defined(LINUX) || defined(NTO) || defined(DARWIN) || defined(__riscos__)
     30 #undef HAVE_LCHOWN
     31 #endif
     32 
     33 #define HAVE_FCHMOD
     34 
     35 #ifdef LINUX
     36 #include <getopt.h>
     37 #endif
     38 
     39 #if defined(NCR)
     40 #if !defined(S_ISLNK) && defined(S_IFLNK)
     41 #define S_ISLNK(a)	(((a) & S_IFMT) == S_IFLNK)
     42 #endif
     43 #endif
     44 
     45 #ifdef GETCWD_CANT_MALLOC
     46 /*
     47 * this should probably go into a utility library in case other applications
     48 * need it.
     49 */
     50 static char *
     51 getcwd_do_malloc(char *path, int len) {
     52 
     53    if (!path) {
     54 path = malloc(PATH_MAX +1);
     55 if (!path) return NULL;
     56    }
     57    return getcwd(path, PATH_MAX);
     58 }
     59 #define GETCWD	getcwd_do_malloc
     60 #else
     61 #define GETCWD	getcwd
     62 #endif
     63 
     64 
     65 static void
     66 usage(void)
     67 {
     68    fprintf(stderr,
     69 "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n"
     70 "       %*s [-DdltR] file [file ...] directory\n",
     71 program, (int)strlen(program), "");
     72    exit(2);
     73 }
     74 
     75 /* this is more-or-less equivalent to mkdir -p */
     76 static int
     77 mkdirs(char *path, mode_t mode)
     78 {
     79    char *      cp;
     80    int         rv;
     81    struct stat sb;
     82 
     83    if (!path || !path[0])
     84 fail("Null pointer or empty string passed to mkdirs()");
     85    while (*path == '/' && path[1] == '/')
     86 path++;
     87    for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--);
     88    if (cp && cp != path) {
     89 *cp = '\0';
     90 if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
     91     mkdirs(path, mode) < 0) {
     92     return -1;
     93 }
     94 *cp = '/';
     95    }
     96    rv = mkdir(path, mode);
     97    if (rv) {
     98 if (errno != EEXIST)
     99     fail("mkdirs cannot make %s", path);
    100 fprintf(stderr, "directory creation race: %s\n", path);
    101 if (!stat(path, &sb) && S_ISDIR(sb.st_mode))
    102     rv = 0;
    103    }
    104    return rv;
    105 }
    106 
    107 static uid_t
    108 touid(char *owner)
    109 {
    110    struct passwd *pw;
    111    uid_t uid;
    112    char *cp;
    113 
    114    if (!owner || !owner[0])
    115 fail("Null pointer or empty string passed to touid()");
    116    pw = getpwnam(owner);
    117    if (pw)
    118 return pw->pw_uid;
    119    uid = strtol(owner, &cp, 0);
    120    if (uid == 0 && cp == owner)
    121 fail("cannot find uid for %s", owner);
    122    return uid;
    123 }
    124 
    125 static gid_t
    126 togid(char *group)
    127 {
    128    struct group *gr;
    129    gid_t gid;
    130    char *cp;
    131 
    132    if (!group || !group[0])
    133 fail("Null pointer or empty string passed to togid()");
    134    gr = getgrnam(group);
    135    if (gr)
    136 return gr->gr_gid;
    137    gid = strtol(group, &cp, 0);
    138    if (gid == 0 && cp == group)
    139 fail("cannot find gid for %s", group);
    140    return gid;
    141 }
    142 
    143 void * const uninit = (void *)0xdeadbeef;
    144 
    145 int
    146 main(int argc, char **argv)
    147 {
    148    char *	base		= uninit;
    149    char *	bp		= uninit;
    150    char *	cp		= uninit;
    151    char *	cwd		= 0;
    152    char *	group		= 0;
    153    char *	linkname	= 0;
    154    char *	linkprefix	= 0;
    155    char *	name		= uninit;
    156    char *	owner		= 0;
    157    char *	todir		= uninit;
    158    char *	toname		= uninit;
    159 
    160    int 	bnlen		= -1;
    161    int 	cc		= 0;
    162    int 	dodir		= 0;
    163    int 	dolink		= 0;
    164    int 	dorelsymlink	= 0;
    165    int 	dotimes		= 0;
    166    int 	exists		= 0;
    167    int 	fromfd		= -1;
    168    int 	len		= -1;
    169    int 	lplen		= 0;
    170    int		onlydir		= 0;
    171    int 	opt		= -1;
    172    int 	tdlen		= -1;
    173    int 	tofd		= -1;
    174    int 	wc		= -1;
    175 
    176    mode_t 	mode		= 0755;
    177 
    178    uid_t 	uid		= -1;
    179    gid_t 	gid		= -1;
    180 
    181    struct stat sb;
    182    struct stat tosb;
    183    struct utimbuf utb;
    184    char 	buf[BUFSIZ];
    185 
    186    program = strrchr(argv[0], '/');
    187    if (!program)
    188 program = strrchr(argv[0], '\\');
    189    program = program ? program+1 : argv[0];
    190 
    191 
    192    while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) {
    193 switch (opt) {
    194   case 'C': cwd = optarg;	break;
    195   case 'D': onlydir = 1; 	break;
    196   case 'd': dodir = 1; 		break;
    197   case 'l': dolink = 1;		break;
    198   case 'L':
    199     linkprefix = optarg;
    200     lplen = strlen(linkprefix);
    201     dolink = 1;
    202     break;
    203   case 'R': dolink = dorelsymlink = 1; break;
    204   case 'm':
    205     mode = strtoul(optarg, &cp, 8);
    206     if (mode == 0 && cp == optarg)
    207 	usage();
    208     break;
    209   case 'o': owner = optarg; 	break;
    210   case 'g': group = optarg; 	break;
    211   case 't': dotimes = 1; 	break;
    212   default:  usage();
    213 }
    214    }
    215 
    216    argc -= optind;
    217    argv += optind;
    218    if (argc < 2 - onlydir)
    219 usage();
    220 
    221    todir = argv[argc-1];
    222    if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) &&
    223 mkdirs(todir, 0777) < 0) {
    224 fail("cannot mkdir -p %s", todir);
    225    }
    226    if (onlydir)
    227 return 0;
    228 
    229    if (!cwd) {
    230 cwd = GETCWD(0, PATH_MAX);
    231 if (!cwd)
    232     fail("could not get CWD");
    233    }
    234 
    235    /* make sure we can get into todir. */
    236    xchdir(todir);
    237    todir = GETCWD(0, PATH_MAX);
    238    if (!todir)
    239 fail("could not get CWD in todir");
    240    tdlen = strlen(todir);
    241 
    242    /* back to original directory. */
    243    xchdir(cwd);
    244 
    245    uid = owner ? touid(owner) : -1;
    246    gid = group ? togid(group) : -1;
    247 
    248    while (--argc > 0) {
    249 name   = *argv++;
    250 len    = strlen(name);
    251 base   = xbasename(name);
    252 bnlen  = strlen(base);
    253    size_t toname_len = tdlen + 1 + bnlen + 1;
    254 toname = (char*)xmalloc(toname_len);
    255 snprintf(toname, toname_len, "%s/%s", todir, base);
    256 retry:
    257 exists = (lstat(toname, &tosb) == 0);
    258 
    259 if (dodir) {
    260     /* -d means create a directory, always */
    261     if (exists && !S_ISDIR(tosb.st_mode)) {
    262 	int rv = unlink(toname);
    263 	if (rv)
    264 	    fail("cannot unlink %s", toname);
    265 	exists = 0;
    266     }
    267     if (!exists && mkdir(toname, mode) < 0) {
    268     	/* we probably have two nsinstall programs in a race here. */
    269 	if (errno == EEXIST && !stat(toname, &sb) &&
    270 	    S_ISDIR(sb.st_mode)) {
    271 	    fprintf(stderr, "directory creation race: %s\n", toname);
    272 	    goto retry;
    273     	}
    274 	fail("cannot make directory %s", toname);
    275     }
    276     if ((owner || group) && chown(toname, uid, gid) < 0)
    277 	fail("cannot change owner of %s", toname);
    278 } else if (dolink) {
    279     if (*name == '/') {
    280 	/* source is absolute pathname, link to it directly */
    281 	linkname = 0;
    282     } else {
    283 	if (linkprefix) {
    284 	    /* -L implies -l and prefixes names with a $cwd arg. */
    285 	    len += lplen + 1;
    286 	    linkname = (char*)xmalloc(len + 1);
    287 	    snprintf(linkname, len+1, "%s/%s", linkprefix, name);
    288 	} else if (dorelsymlink) {
    289 	    /* Symlink the relative path from todir to source name. */
    290 	    linkname = (char*)xmalloc(PATH_MAX);
    291 
    292 	    if (*todir == '/') {
    293 		/* todir is absolute: skip over common prefix. */
    294 		lplen = relatepaths(todir, cwd, linkname);
    295 		strcpy(linkname + lplen, name);
    296 	    } else {
    297 		/* todir is named by a relative path: reverse it. */
    298 		reversepath(todir, name, len, linkname);
    299 		xchdir(cwd);
    300 	    }
    301 
    302 	    len = strlen(linkname);
    303 	}
    304 	name = linkname;
    305     }
    306 
    307     /* Check for a pre-existing symlink with identical content. */
    308     if (exists &&
    309 	(!S_ISLNK(tosb.st_mode) ||
    310 	 readlink(toname, buf, sizeof buf) != len ||
    311 	 strncmp(buf, name, len) != 0)) {
    312 	int rmrv;
    313 	rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
    314 	if (rmrv < 0) {
    315 	    fail("destination exists, cannot remove %s", toname);
    316 	}
    317 	exists = 0;
    318     }
    319     if (!exists && symlink(name, toname) < 0) {
    320 	if (errno == EEXIST) {
    321 	    fprintf(stderr, "symlink creation race: %s\n", toname);
    322                    fail("symlink was attempted in working directory %s "
    323                         "from %s to %s.\n", cwd, name, toname);
    324 	    goto retry;
    325 	}
    326 	diagnosePath(toname);
    327 	fail("cannot make symbolic link %s", toname);
    328     }
    329 #ifdef HAVE_LCHOWN
    330     if ((owner || group) && lchown(toname, uid, gid) < 0)
    331 	fail("cannot change owner of %s", toname);
    332 #endif
    333 
    334     if (linkname) {
    335 	free(linkname);
    336 	linkname = 0;
    337     }
    338 } else {
    339     /* Copy from name to toname, which might be the same file. */
    340     fromfd = open(name, O_RDONLY);
    341     if (fromfd < 0 || fstat(fromfd, &sb) < 0)
    342 	fail("cannot access %s", name);
    343     if (exists &&
    344         (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) {
    345 	int rmrv;
    346 	rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname);
    347 	if (rmrv < 0) {
    348 	    fail("destination exists, cannot remove %s", toname);
    349 	}
    350     }
    351     tofd = open(toname, O_CREAT | O_WRONLY, 0666);
    352     if (tofd < 0)
    353 	fail("cannot create %s", toname);
    354 
    355     bp = buf;
    356     while ((cc = read(fromfd, bp, sizeof buf)) > 0) {
    357 	while ((wc = write(tofd, bp, cc)) > 0) {
    358 	    if ((cc -= wc) == 0)
    359 		break;
    360 	    bp += wc;
    361 	}
    362 	if (wc < 0)
    363 	    fail("cannot write to %s", toname);
    364     }
    365     if (cc < 0)
    366 	fail("cannot read from %s", name);
    367 
    368     if (ftruncate(tofd, sb.st_size) < 0)
    369 	fail("cannot truncate %s", toname);
    370     if (dotimes) {
    371 	utb.actime = sb.st_atime;
    372 	utb.modtime = sb.st_mtime;
    373 	if (utime(toname, &utb) < 0)
    374 	    fail("cannot set times of %s", toname);
    375     }
    376 #ifdef HAVE_FCHMOD
    377     if (fchmod(tofd, mode) < 0)
    378 #else
    379     if (chmod(toname, mode) < 0)
    380 #endif
    381 	fail("cannot change mode of %s", toname);
    382 
    383     if ((owner || group) && fchown(tofd, uid, gid) < 0)
    384 	fail("cannot change owner of %s", toname);
    385 
    386     /* Must check for delayed (NFS) write errors on close. */
    387     if (close(tofd) < 0)
    388 	fail("close reports write error on %s", toname);
    389     close(fromfd);
    390 }
    391 
    392 free(toname);
    393    }
    394 
    395    free(cwd);
    396    free(todir);
    397    return 0;
    398 }