michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: ** Netscape portable install command. michael@0: */ michael@0: #include /* OSF/1 requires this before grp.h, so put it first */ michael@0: #include michael@0: #include michael@0: #include michael@0: #if defined(_WINDOWS) michael@0: #include michael@0: typedef unsigned int mode_t; michael@0: #else michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #endif michael@0: #include michael@0: #include michael@0: #include "pathsub.h" michael@0: michael@0: #define HAVE_LCHOWN michael@0: michael@0: #if defined(AIX) || defined(BSDI) || defined(HPUX) || defined(LINUX) || defined(SUNOS4) || defined(SCO) || defined(UNIXWARE) || defined(NTO) || defined(DARWIN) || defined(BEOS) || defined(__riscos__) michael@0: #undef HAVE_LCHOWN michael@0: #endif michael@0: michael@0: #define HAVE_FCHMOD michael@0: michael@0: #if defined(BEOS) michael@0: #undef HAVE_FCHMOD michael@0: #endif michael@0: michael@0: #ifdef LINUX michael@0: #include michael@0: #endif michael@0: michael@0: #if defined(SCO) || defined(UNIXWARE) || defined(SNI) || defined(NCR) || defined(NEC) michael@0: #if !defined(S_ISLNK) && defined(S_IFLNK) michael@0: #define S_ISLNK(a) (((a) & S_IFMT) == S_IFLNK) michael@0: #endif michael@0: #endif michael@0: michael@0: #if defined(SNI) michael@0: extern int fchmod(int fildes, mode_t mode); michael@0: #endif michael@0: michael@0: michael@0: #ifdef GETCWD_CANT_MALLOC michael@0: /* michael@0: * this should probably go into a utility library in case other applications michael@0: * need it. michael@0: */ michael@0: static char * michael@0: getcwd_do_malloc(char *path, int len) { michael@0: michael@0: if (!path) { michael@0: path = malloc(PATH_MAX +1); michael@0: if (!path) return NULL; michael@0: } michael@0: return getcwd(path, PATH_MAX); michael@0: } michael@0: #define GETCWD getcwd_do_malloc michael@0: #else michael@0: #define GETCWD getcwd michael@0: #endif michael@0: michael@0: michael@0: static void michael@0: usage(void) michael@0: { michael@0: fprintf(stderr, michael@0: "usage: %s [-C cwd] [-L linkprefix] [-m mode] [-o owner] [-g group]\n" michael@0: " %*s [-DdltR] file [file ...] directory\n", michael@0: program, (int)strlen(program), ""); michael@0: exit(2); michael@0: } michael@0: michael@0: /* this is more-or-less equivalent to mkdir -p */ michael@0: static int michael@0: mkdirs(char *path, mode_t mode) michael@0: { michael@0: char * cp; michael@0: int rv; michael@0: struct stat sb; michael@0: michael@0: if (!path || !path[0]) michael@0: fail("Null pointer or empty string passed to mkdirs()"); michael@0: while (*path == '/' && path[1] == '/') michael@0: path++; michael@0: for (cp = strrchr(path, '/'); cp && cp != path && *(cp - 1) == '/'; cp--); michael@0: if (cp && cp != path) { michael@0: *cp = '\0'; michael@0: if ((stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode)) && michael@0: mkdirs(path, mode) < 0) { michael@0: return -1; michael@0: } michael@0: *cp = '/'; michael@0: } michael@0: rv = mkdir(path, mode); michael@0: if (rv) { michael@0: if (errno != EEXIST) michael@0: fail("mkdirs cannot make %s", path); michael@0: fprintf(stderr, "directory creation race: %s\n", path); michael@0: if (!stat(path, &sb) && S_ISDIR(sb.st_mode)) michael@0: rv = 0; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: static uid_t michael@0: touid(char *owner) michael@0: { michael@0: struct passwd *pw; michael@0: uid_t uid; michael@0: char *cp; michael@0: michael@0: if (!owner || !owner[0]) michael@0: fail("Null pointer or empty string passed to touid()"); michael@0: pw = getpwnam(owner); michael@0: if (pw) michael@0: return pw->pw_uid; michael@0: uid = strtol(owner, &cp, 0); michael@0: if (uid == 0 && cp == owner) michael@0: fail("cannot find uid for %s", owner); michael@0: return uid; michael@0: } michael@0: michael@0: static gid_t michael@0: togid(char *group) michael@0: { michael@0: struct group *gr; michael@0: gid_t gid; michael@0: char *cp; michael@0: michael@0: if (!group || !group[0]) michael@0: fail("Null pointer or empty string passed to togid()"); michael@0: gr = getgrnam(group); michael@0: if (gr) michael@0: return gr->gr_gid; michael@0: gid = strtol(group, &cp, 0); michael@0: if (gid == 0 && cp == group) michael@0: fail("cannot find gid for %s", group); michael@0: return gid; michael@0: } michael@0: michael@0: void * const uninit = (void *)0xdeadbeef; michael@0: michael@0: int michael@0: main(int argc, char **argv) michael@0: { michael@0: char * base = uninit; michael@0: char * bp = uninit; michael@0: char * cp = uninit; michael@0: char * cwd = 0; michael@0: char * group = 0; michael@0: char * linkname = 0; michael@0: char * linkprefix = 0; michael@0: char * name = uninit; michael@0: char * owner = 0; michael@0: char * todir = uninit; michael@0: char * toname = uninit; michael@0: michael@0: int bnlen = -1; michael@0: int cc = 0; michael@0: int dodir = 0; michael@0: int dolink = 0; michael@0: int dorelsymlink = 0; michael@0: int dotimes = 0; michael@0: int exists = 0; michael@0: int fromfd = -1; michael@0: int len = -1; michael@0: int lplen = 0; michael@0: int onlydir = 0; michael@0: int opt = -1; michael@0: int tdlen = -1; michael@0: int tofd = -1; michael@0: int wc = -1; michael@0: michael@0: mode_t mode = 0755; michael@0: michael@0: uid_t uid = -1; michael@0: gid_t gid = -1; michael@0: michael@0: struct stat sb; michael@0: struct stat tosb; michael@0: struct utimbuf utb; michael@0: char buf[BUFSIZ]; michael@0: michael@0: program = strrchr(argv[0], '/'); michael@0: if (!program) michael@0: program = strrchr(argv[0], '\\'); michael@0: program = program ? program+1 : argv[0]; michael@0: michael@0: michael@0: while ((opt = getopt(argc, argv, "C:DdlL:Rm:o:g:t")) != EOF) { michael@0: switch (opt) { michael@0: case 'C': cwd = optarg; break; michael@0: case 'D': onlydir = 1; break; michael@0: case 'd': dodir = 1; break; michael@0: case 'l': dolink = 1; break; michael@0: case 'L': michael@0: linkprefix = optarg; michael@0: lplen = strlen(linkprefix); michael@0: dolink = 1; michael@0: break; michael@0: case 'R': dolink = dorelsymlink = 1; break; michael@0: case 'm': michael@0: mode = strtoul(optarg, &cp, 8); michael@0: if (mode == 0 && cp == optarg) michael@0: usage(); michael@0: break; michael@0: case 'o': owner = optarg; break; michael@0: case 'g': group = optarg; break; michael@0: case 't': dotimes = 1; break; michael@0: default: usage(); michael@0: } michael@0: } michael@0: michael@0: argc -= optind; michael@0: argv += optind; michael@0: if (argc < 2 - onlydir) michael@0: usage(); michael@0: michael@0: todir = argv[argc-1]; michael@0: if ((stat(todir, &sb) < 0 || !S_ISDIR(sb.st_mode)) && michael@0: mkdirs(todir, 0777) < 0) { michael@0: fail("cannot mkdir -p %s", todir); michael@0: } michael@0: if (onlydir) michael@0: return 0; michael@0: michael@0: if (!cwd) { michael@0: cwd = GETCWD(0, PATH_MAX); michael@0: if (!cwd) michael@0: fail("could not get CWD"); michael@0: } michael@0: michael@0: /* make sure we can get into todir. */ michael@0: xchdir(todir); michael@0: todir = GETCWD(0, PATH_MAX); michael@0: if (!todir) michael@0: fail("could not get CWD in todir"); michael@0: tdlen = strlen(todir); michael@0: michael@0: /* back to original directory. */ michael@0: xchdir(cwd); michael@0: michael@0: uid = owner ? touid(owner) : -1; michael@0: gid = group ? togid(group) : -1; michael@0: michael@0: while (--argc > 0) { michael@0: name = *argv++; michael@0: len = strlen(name); michael@0: base = xbasename(name); michael@0: bnlen = strlen(base); michael@0: toname = (char*)xmalloc(tdlen + 1 + bnlen + 1); michael@0: sprintf(toname, "%s/%s", todir, base); michael@0: retry: michael@0: exists = (lstat(toname, &tosb) == 0); michael@0: michael@0: if (dodir) { michael@0: /* -d means create a directory, always */ michael@0: if (exists && !S_ISDIR(tosb.st_mode)) { michael@0: int rv = unlink(toname); michael@0: if (rv) michael@0: fail("cannot unlink %s", toname); michael@0: exists = 0; michael@0: } michael@0: if (!exists && mkdir(toname, mode) < 0) { michael@0: /* we probably have two nsinstall programs in a race here. */ michael@0: if (errno == EEXIST && !stat(toname, &sb) && michael@0: S_ISDIR(sb.st_mode)) { michael@0: fprintf(stderr, "directory creation race: %s\n", toname); michael@0: goto retry; michael@0: } michael@0: fail("cannot make directory %s", toname); michael@0: } michael@0: if ((owner || group) && chown(toname, uid, gid) < 0) michael@0: fail("cannot change owner of %s", toname); michael@0: } else if (dolink) { michael@0: if (*name == '/') { michael@0: /* source is absolute pathname, link to it directly */ michael@0: linkname = 0; michael@0: } else { michael@0: if (linkprefix) { michael@0: /* -L implies -l and prefixes names with a $cwd arg. */ michael@0: len += lplen + 1; michael@0: linkname = (char*)xmalloc(len + 1); michael@0: sprintf(linkname, "%s/%s", linkprefix, name); michael@0: } else if (dorelsymlink) { michael@0: /* Symlink the relative path from todir to source name. */ michael@0: linkname = (char*)xmalloc(PATH_MAX); michael@0: michael@0: if (*todir == '/') { michael@0: /* todir is absolute: skip over common prefix. */ michael@0: lplen = relatepaths(todir, cwd, linkname); michael@0: strcpy(linkname + lplen, name); michael@0: } else { michael@0: /* todir is named by a relative path: reverse it. */ michael@0: reversepath(todir, name, len, linkname); michael@0: xchdir(cwd); michael@0: } michael@0: michael@0: len = strlen(linkname); michael@0: } michael@0: name = linkname; michael@0: } michael@0: michael@0: /* Check for a pre-existing symlink with identical content. */ michael@0: if (exists && michael@0: (!S_ISLNK(tosb.st_mode) || michael@0: readlink(toname, buf, sizeof buf) != len || michael@0: strncmp(buf, name, len) != 0)) { michael@0: int rmrv; michael@0: rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); michael@0: if (rmrv < 0) { michael@0: fail("destination exists, cannot remove %s", toname); michael@0: } michael@0: exists = 0; michael@0: } michael@0: if (!exists && symlink(name, toname) < 0) { michael@0: if (errno == EEXIST) { michael@0: fprintf(stderr, "symlink creation race: %s\n", toname); michael@0: fail("symlink was attempted in working directory %s " michael@0: "from %s to %s.\n", cwd, name, toname); michael@0: goto retry; michael@0: } michael@0: diagnosePath(toname); michael@0: fail("cannot make symbolic link %s", toname); michael@0: } michael@0: #ifdef HAVE_LCHOWN michael@0: if ((owner || group) && lchown(toname, uid, gid) < 0) michael@0: fail("cannot change owner of %s", toname); michael@0: #endif michael@0: michael@0: if (linkname) { michael@0: free(linkname); michael@0: linkname = 0; michael@0: } michael@0: } else { michael@0: /* Copy from name to toname, which might be the same file. */ michael@0: fromfd = open(name, O_RDONLY); michael@0: if (fromfd < 0 || fstat(fromfd, &sb) < 0) michael@0: fail("cannot access %s", name); michael@0: if (exists && michael@0: (!S_ISREG(tosb.st_mode) || access(toname, W_OK) < 0)) { michael@0: int rmrv; michael@0: rmrv = (S_ISDIR(tosb.st_mode) ? rmdir : unlink)(toname); michael@0: if (rmrv < 0) { michael@0: fail("destination exists, cannot remove %s", toname); michael@0: } michael@0: } michael@0: tofd = open(toname, O_CREAT | O_WRONLY, 0666); michael@0: if (tofd < 0) michael@0: fail("cannot create %s", toname); michael@0: michael@0: bp = buf; michael@0: while ((cc = read(fromfd, bp, sizeof buf)) > 0) { michael@0: while ((wc = write(tofd, bp, cc)) > 0) { michael@0: if ((cc -= wc) == 0) michael@0: break; michael@0: bp += wc; michael@0: } michael@0: if (wc < 0) michael@0: fail("cannot write to %s", toname); michael@0: } michael@0: if (cc < 0) michael@0: fail("cannot read from %s", name); michael@0: michael@0: if (ftruncate(tofd, sb.st_size) < 0) michael@0: fail("cannot truncate %s", toname); michael@0: if (dotimes) { michael@0: utb.actime = sb.st_atime; michael@0: utb.modtime = sb.st_mtime; michael@0: if (utime(toname, &utb) < 0) michael@0: fail("cannot set times of %s", toname); michael@0: } michael@0: #ifdef HAVE_FCHMOD michael@0: if (fchmod(tofd, mode) < 0) michael@0: #else michael@0: if (chmod(toname, mode) < 0) michael@0: #endif michael@0: fail("cannot change mode of %s", toname); michael@0: michael@0: if ((owner || group) && fchown(tofd, uid, gid) < 0) michael@0: fail("cannot change owner of %s", toname); michael@0: michael@0: /* Must check for delayed (NFS) write errors on close. */ michael@0: if (close(tofd) < 0) michael@0: fail("close reports write error on %s", toname); michael@0: close(fromfd); michael@0: } michael@0: michael@0: free(toname); michael@0: } michael@0: michael@0: free(cwd); michael@0: free(todir); michael@0: return 0; michael@0: } michael@0: