e547f8e83a
copy. Dont leave stray INS@xxxx temp files around, especially when installing something less than 8MB and the destination runs out of space, etc. It still doesn't clean up the temp files on SEGV or other signals etc.
651 lines
16 KiB
C
651 lines
16 KiB
C
/*
|
|
* Copyright (c) 1987, 1993
|
|
* The Regents of the University of California. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
* must display the following acknowledgement:
|
|
* This product includes software developed by the University of
|
|
* California, Berkeley and its contributors.
|
|
* 4. Neither the name of the University nor the names of its contributors
|
|
* may be used to endorse or promote products derived from this software
|
|
* without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
* SUCH DAMAGE.
|
|
*/
|
|
|
|
#ifndef lint
|
|
static const char copyright[] =
|
|
"@(#) Copyright (c) 1987, 1993\n\
|
|
The Regents of the University of California. All rights reserved.\n";
|
|
#endif /* not lint */
|
|
|
|
#ifndef lint
|
|
/*static char sccsid[] = "From: @(#)xinstall.c 8.1 (Berkeley) 7/21/93";*/
|
|
static const char rcsid[] =
|
|
"$Id: xinstall.c,v 1.11 1996/09/05 07:33:24 peter Exp $";
|
|
#endif /* not lint */
|
|
|
|
/*-
|
|
* Todo:
|
|
* o for -C, compare original files except in -s case.
|
|
* o for -C, don't change anything if nothing needs be changed. In
|
|
* particular, don't toggle the immutable flags just to allow null
|
|
* attribute changes and don't clear the dump flag. (I think inode
|
|
* ctimes are not updated for null attribute changes, but this is a
|
|
* bug.)
|
|
* o independent of -C, if a copy must be made, then copy to a tmpfile,
|
|
* set all attributes except the immutable flags, then rename, then
|
|
* set the immutable flags. It's annoying that the immutable flags
|
|
* defeat the atomicicity of rename - it seems that there must be
|
|
* o a window where the target is not immutable.
|
|
*/
|
|
|
|
#include <sys/param.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/mount.h>
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <grp.h>
|
|
#include <paths.h>
|
|
#include <pwd.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sysexits.h>
|
|
#include <unistd.h>
|
|
#include <utime.h>
|
|
|
|
#include "pathnames.h"
|
|
|
|
int debug, docompare, docopy, dopreserve, dostrip, verbose;
|
|
int mode = S_IRWXU|S_IRGRP|S_IXGRP|S_IROTH|S_IXOTH;
|
|
char *group, *owner, pathbuf[MAXPATHLEN];
|
|
char pathbuf2[MAXPATHLEN];
|
|
|
|
#define DIRECTORY 0x01 /* Tell install it's a directory. */
|
|
#define SETFLAGS 0x02 /* Tell install to set flags. */
|
|
#define NOCHANGEBITS (UF_IMMUTABLE | UF_APPEND | SF_IMMUTABLE | SF_APPEND)
|
|
|
|
void copy __P((int, char *, int, char *, off_t));
|
|
int compare __P((int, const char *, int, const char *,
|
|
const struct stat *, const struct stat *));
|
|
void install __P((char *, char *, u_long, u_int));
|
|
u_long string_to_flags __P((char **, u_long *, u_long *));
|
|
void strip __P((char *));
|
|
void usage __P((void));
|
|
int trymmap __P((int));
|
|
|
|
#define ALLOW_NUMERIC_IDS 1
|
|
#ifdef ALLOW_NUMERIC_IDS
|
|
|
|
uid_t uid;
|
|
gid_t gid;
|
|
|
|
uid_t resolve_uid __P((char *));
|
|
gid_t resolve_gid __P((char *));
|
|
u_long numeric_id __P((char *, char *));
|
|
|
|
#else
|
|
|
|
struct passwd *pp;
|
|
struct group *gp;
|
|
|
|
#endif /* ALLOW_NUMERIC_IDS */
|
|
|
|
int
|
|
main(argc, argv)
|
|
int argc;
|
|
char *argv[];
|
|
{
|
|
struct stat from_sb, to_sb;
|
|
mode_t *set;
|
|
u_long fset;
|
|
u_int iflags;
|
|
int ch, no_target;
|
|
char *flags, *to_name;
|
|
|
|
iflags = 0;
|
|
while ((ch = getopt(argc, argv, "Ccdf:g:m:o:psv")) != EOF)
|
|
switch((char)ch) {
|
|
case 'C':
|
|
docompare = docopy = 1;
|
|
break;
|
|
case 'c':
|
|
docopy = 1;
|
|
break;
|
|
case 'd':
|
|
debug++;
|
|
break;
|
|
case 'f':
|
|
flags = optarg;
|
|
if (string_to_flags(&flags, &fset, NULL))
|
|
errx(EX_USAGE, "%s: invalid flag", flags);
|
|
iflags |= SETFLAGS;
|
|
break;
|
|
case 'g':
|
|
group = optarg;
|
|
break;
|
|
case 'm':
|
|
if (!(set = setmode(optarg)))
|
|
errx(EX_USAGE, "invalid file mode: %s",
|
|
optarg);
|
|
mode = getmode(set, 0);
|
|
break;
|
|
case 'o':
|
|
owner = optarg;
|
|
break;
|
|
case 'p':
|
|
docompare = docopy = dopreserve = 1;
|
|
break;
|
|
case 's':
|
|
dostrip = 1;
|
|
break;
|
|
case 'v':
|
|
verbose = 1;
|
|
break;
|
|
case '?':
|
|
default:
|
|
usage();
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
if (argc < 2)
|
|
usage();
|
|
|
|
#ifdef ALLOW_NUMERIC_IDS
|
|
|
|
if (owner)
|
|
uid = resolve_uid(owner);
|
|
if (group)
|
|
gid = resolve_gid(group);
|
|
|
|
#else
|
|
|
|
/* get group and owner id's */
|
|
if (owner && !(pp = getpwnam(owner)))
|
|
errx(EX_NOUSER, "unknown user %s", owner);
|
|
if (group && !(gp = getgrnam(group)))
|
|
errx(EX_NOUSER, "unknown group %s", group);
|
|
|
|
#endif /* ALLOW_NUMERIC_IDS */
|
|
|
|
no_target = stat(to_name = argv[argc - 1], &to_sb);
|
|
if (!no_target && (to_sb.st_mode & S_IFMT) == S_IFDIR) {
|
|
for (; *argv != to_name; ++argv)
|
|
install(*argv, to_name, fset, iflags | DIRECTORY);
|
|
exit(0);
|
|
}
|
|
|
|
/* can't do file1 file2 directory/file */
|
|
if (argc != 2)
|
|
usage();
|
|
|
|
if (!no_target) {
|
|
if (stat(*argv, &from_sb))
|
|
err(EX_OSERR, "%s", *argv);
|
|
if (!S_ISREG(to_sb.st_mode)) {
|
|
errno = EFTYPE;
|
|
err(EX_OSERR, "%s", to_name);
|
|
}
|
|
if (to_sb.st_dev == from_sb.st_dev &&
|
|
to_sb.st_ino == from_sb.st_ino)
|
|
errx(EX_USAGE,
|
|
"%s and %s are the same file", *argv, to_name);
|
|
/*
|
|
* XXX - It's not at all clear why this code was here, since it completely
|
|
* duplicates code install(). The version in install() handles the -C flag
|
|
* correctly, so we'll just disable this for now.
|
|
*/
|
|
#if 0
|
|
/*
|
|
* Unlink now... avoid ETXTBSY errors later. Try and turn
|
|
* off the append/immutable bits -- if we fail, go ahead,
|
|
* it might work.
|
|
*/
|
|
if (to_sb.st_flags & NOCHANGEBITS)
|
|
(void)chflags(to_name,
|
|
to_sb.st_flags & ~(NOCHANGEBITS));
|
|
(void)unlink(to_name);
|
|
#endif
|
|
}
|
|
install(*argv, to_name, fset, iflags);
|
|
exit(0);
|
|
}
|
|
|
|
#ifdef ALLOW_NUMERIC_IDS
|
|
|
|
uid_t
|
|
resolve_uid(s)
|
|
char *s;
|
|
{
|
|
struct passwd *pw;
|
|
|
|
return ((pw = getpwnam(s)) == NULL) ?
|
|
(uid_t) numeric_id(s, "user") : pw->pw_uid;
|
|
}
|
|
|
|
gid_t
|
|
resolve_gid(s)
|
|
char *s;
|
|
{
|
|
struct group *gr;
|
|
|
|
return ((gr = getgrnam(s)) == NULL) ?
|
|
(gid_t) numeric_id(s, "group") : gr->gr_gid;
|
|
}
|
|
|
|
u_long
|
|
numeric_id(name, type)
|
|
char *name, *type;
|
|
{
|
|
u_long val;
|
|
char *ep;
|
|
|
|
/*
|
|
* XXX
|
|
* We know that uid_t's and gid_t's are unsigned longs.
|
|
*/
|
|
errno = 0;
|
|
val = strtoul(name, &ep, 10);
|
|
if (errno)
|
|
err(EX_NOUSER, "%s", name);
|
|
if (*ep != '\0')
|
|
errx(EX_NOUSER, "unknown %s %s", type, name);
|
|
return (val);
|
|
}
|
|
|
|
#endif /* ALLOW_NUMERIC_IDS */
|
|
|
|
/*
|
|
* install --
|
|
* build a path name and install the file
|
|
*/
|
|
void
|
|
install(from_name, to_name, fset, flags)
|
|
char *from_name, *to_name;
|
|
u_long fset;
|
|
u_int flags;
|
|
{
|
|
struct stat from_sb, to_sb;
|
|
int devnull, from_fd, to_fd, serrno;
|
|
char *p, *old_to_name = 0;
|
|
|
|
if (debug >= 2 && !docompare)
|
|
fprintf(stderr, "install: invoked without -C for %s to %s\n",
|
|
from_name, to_name);
|
|
|
|
/* If try to install NULL file to a directory, fails. */
|
|
if (flags & DIRECTORY || strcmp(from_name, _PATH_DEVNULL)) {
|
|
if (stat(from_name, &from_sb))
|
|
err(EX_OSERR, "%s", from_name);
|
|
if (!S_ISREG(from_sb.st_mode)) {
|
|
errno = EFTYPE;
|
|
err(EX_OSERR, "%s", from_name);
|
|
}
|
|
/* Build the target path. */
|
|
if (flags & DIRECTORY) {
|
|
(void)snprintf(pathbuf, sizeof(pathbuf), "%s/%s",
|
|
to_name,
|
|
(p = strrchr(from_name, '/')) ? ++p : from_name);
|
|
to_name = pathbuf;
|
|
}
|
|
devnull = 0;
|
|
} else {
|
|
from_sb.st_flags = 0; /* XXX */
|
|
devnull = 1;
|
|
}
|
|
|
|
if (docompare) {
|
|
old_to_name = to_name;
|
|
/*
|
|
* Make a new temporary file in the same file system
|
|
* (actually, in in the same directory) as the target so
|
|
* that the temporary file can be renamed to the target.
|
|
*/
|
|
snprintf(pathbuf2, sizeof pathbuf2, "%s", to_name);
|
|
p = strrchr(pathbuf2, '/');
|
|
p = (p == NULL ? pathbuf2 : p + 1);
|
|
snprintf(p, &pathbuf2[sizeof pathbuf2] - p, "INS@XXXX");
|
|
to_fd = mkstemp(pathbuf2);
|
|
if (to_fd < 0)
|
|
/* XXX should fall back to not comparing. */
|
|
err(EX_OSERR, "mkstemp: %s for %s", pathbuf2, to_name);
|
|
to_name = pathbuf2;
|
|
} else {
|
|
/*
|
|
* Unlink now... avoid errors later. Try to turn off the
|
|
* append/immutable bits -- if we fail, go ahead, it might
|
|
* work.
|
|
*/
|
|
if (stat(to_name, &to_sb) == 0 && to_sb.st_flags & NOCHANGEBITS)
|
|
(void)chflags(to_name, to_sb.st_flags & ~NOCHANGEBITS);
|
|
unlink(to_name);
|
|
|
|
/* Create target. */
|
|
to_fd = open(to_name,
|
|
O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR);
|
|
if (to_fd < 0)
|
|
err(EX_OSERR, "%s", to_name);
|
|
}
|
|
|
|
if (!devnull) {
|
|
if ((from_fd = open(from_name, O_RDONLY, 0)) < 0) {
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = serrno;
|
|
err(EX_OSERR, "%s", from_name);
|
|
}
|
|
copy(from_fd, from_name, to_fd, to_name, from_sb.st_size);
|
|
(void)close(from_fd);
|
|
}
|
|
|
|
if (dostrip)
|
|
strip(to_name);
|
|
|
|
/*
|
|
* Unfortunately, because we strip the installed file and not the
|
|
* original one, it is impossible to do the comparison without
|
|
* first laboriously copying things over and then comparing.
|
|
* It may be possible to better optimize the !dostrip case, however.
|
|
* For further study.
|
|
*/
|
|
if (docompare) {
|
|
struct stat old_sb, new_sb, timestamp_sb;
|
|
int old_fd;
|
|
struct utimbuf utb;
|
|
|
|
old_fd = open(old_to_name, O_RDONLY, 0);
|
|
if (old_fd < 0 && errno == ENOENT)
|
|
goto different;
|
|
if (old_fd < 0)
|
|
err(EX_OSERR, "%s", old_to_name);
|
|
fstat(old_fd, &old_sb);
|
|
if (old_sb.st_flags & NOCHANGEBITS)
|
|
(void)fchflags(old_fd, old_sb.st_flags & ~NOCHANGEBITS);
|
|
fstat(to_fd, &new_sb);
|
|
if (compare(old_fd, old_to_name, to_fd, to_name, &old_sb,
|
|
&new_sb)) {
|
|
different:
|
|
if (debug != 0)
|
|
fprintf(stderr,
|
|
"install: renaming for %s: %s to %s\n",
|
|
from_name, to_name, old_to_name);
|
|
if (dopreserve && stat(from_name, ×tamp_sb) == 0) {
|
|
utb.actime = from_sb.st_atime;
|
|
utb.modtime = from_sb.st_mtime;
|
|
(void)utime(to_name, &utb);
|
|
}
|
|
moveit:
|
|
if (verbose) {
|
|
printf("install: %s -> %s\n",
|
|
from_name, old_to_name);
|
|
}
|
|
if (rename(to_name, old_to_name) < 0) {
|
|
serrno = errno;
|
|
unlink(to_name);
|
|
unlink(old_to_name);
|
|
errno = serrno;
|
|
err(EX_OSERR, "rename: %s to %s", to_name,
|
|
old_to_name);
|
|
}
|
|
close(old_fd);
|
|
} else {
|
|
if (old_sb.st_nlink != 1) {
|
|
/*
|
|
* Replace the target, although it hasn't
|
|
* changed, to snap the extra links. But
|
|
* preserve the target file times.
|
|
*/
|
|
if (fstat(old_fd, ×tamp_sb) == 0) {
|
|
utb.actime = timestamp_sb.st_atime;
|
|
utb.modtime = timestamp_sb.st_mtime;
|
|
(void)utime(to_name, &utb);
|
|
}
|
|
goto moveit;
|
|
}
|
|
if (unlink(to_name) < 0)
|
|
err(EX_OSERR, "unlink: %s", to_name);
|
|
close(to_fd);
|
|
to_fd = old_fd;
|
|
}
|
|
to_name = old_to_name;
|
|
}
|
|
|
|
/*
|
|
* Set owner, group, mode for target; do the chown first,
|
|
* chown may lose the setuid bits.
|
|
*/
|
|
if ((group || owner) &&
|
|
#ifdef ALLOW_NUMERIC_IDS
|
|
fchown(to_fd, owner ? uid : -1, group ? gid : -1)) {
|
|
#else
|
|
fchown(to_fd, owner ? pp->pw_uid : -1, group ? gp->gr_gid : -1)) {
|
|
#endif
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = serrno;
|
|
err(EX_OSERR,"%s: chown/chgrp", to_name);
|
|
}
|
|
if (fchmod(to_fd, mode)) {
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = serrno;
|
|
err(EX_OSERR, "%s: chmod", to_name);
|
|
}
|
|
|
|
/*
|
|
* If provided a set of flags, set them, otherwise, preserve the
|
|
* flags, except for the dump flag.
|
|
*/
|
|
if (fchflags(to_fd,
|
|
flags & SETFLAGS ? fset : from_sb.st_flags & ~UF_NODUMP)) {
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = serrno;
|
|
err(EX_OSERR, "%s: chflags", to_name);
|
|
}
|
|
|
|
(void)close(to_fd);
|
|
if (!docopy && !devnull && unlink(from_name))
|
|
err(EX_OSERR, "%s", from_name);
|
|
}
|
|
|
|
/*
|
|
* compare --
|
|
* compare two files; non-zero means files differ
|
|
*/
|
|
int
|
|
compare(int from_fd, const char *from_name, int to_fd, const char *to_name,
|
|
const struct stat *from_sb, const struct stat *to_sb)
|
|
{
|
|
char *p, *q;
|
|
int rv;
|
|
size_t tsize;
|
|
int done_compare;
|
|
|
|
if (from_sb->st_size != to_sb->st_size)
|
|
return 1;
|
|
|
|
tsize = (size_t)from_sb->st_size;
|
|
|
|
if (tsize <= 8 * 1024 * 1024) {
|
|
done_compare = 0;
|
|
if (trymmap(from_fd) && trymmap(to_fd)) {
|
|
p = mmap(NULL, tsize, PROT_READ, 0, from_fd, (off_t)0);
|
|
if ((long)p == -1)
|
|
goto out;
|
|
q = mmap(NULL, tsize, PROT_READ, 0, to_fd, (off_t)0);
|
|
if ((long)q == -1) {
|
|
munmap(p, tsize);
|
|
goto out;
|
|
}
|
|
|
|
rv = memcmp(p, q, tsize);
|
|
munmap(p, tsize);
|
|
munmap(q, tsize);
|
|
done_compare = 1;
|
|
}
|
|
out:
|
|
if (!done_compare) {
|
|
char buf1[MAXBSIZE];
|
|
char buf2[MAXBSIZE];
|
|
int n1, n2;
|
|
|
|
rv = 0;
|
|
lseek(from_fd, 0, SEEK_SET);
|
|
lseek(to_fd, 0, SEEK_SET);
|
|
while (rv == 0) {
|
|
n1 = read(from_fd, buf1, sizeof(buf1));
|
|
if (n1 == 0)
|
|
break; /* EOF */
|
|
else if (n1 > 0) {
|
|
n2 = read(to_fd, buf2, n1);
|
|
if (n2 == n1)
|
|
rv = memcmp(buf1, buf2, n1);
|
|
else
|
|
rv = 1; /* out of sync */
|
|
} else
|
|
rv = 1; /* read failure */
|
|
}
|
|
lseek(from_fd, 0, SEEK_SET);
|
|
lseek(to_fd, 0, SEEK_SET);
|
|
}
|
|
} else
|
|
rv = 1; /* don't bother in this case */
|
|
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* copy --
|
|
* copy from one file to another
|
|
*/
|
|
void
|
|
copy(from_fd, from_name, to_fd, to_name, size)
|
|
register int from_fd, to_fd;
|
|
char *from_name, *to_name;
|
|
off_t size;
|
|
{
|
|
register int nr, nw;
|
|
int serrno;
|
|
char *p, buf[MAXBSIZE];
|
|
int done_copy;
|
|
|
|
/*
|
|
* Mmap and write if less than 8M (the limit is so we don't totally
|
|
* trash memory on big files. This is really a minor hack, but it
|
|
* wins some CPU back.
|
|
*/
|
|
done_copy = 0;
|
|
if (size <= 8 * 1048576 && trymmap(from_fd)) {
|
|
if ((p = mmap(NULL, (size_t)size, PROT_READ,
|
|
0, from_fd, (off_t)0)) == (char *)-1)
|
|
goto out;
|
|
if ((nw = write(to_fd, p, size)) != size) {
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = nw > 0 ? EIO : serrno;
|
|
err(EX_OSERR, "%s", to_name);
|
|
}
|
|
done_copy = 1;
|
|
out:
|
|
}
|
|
if (!done_copy) {
|
|
while ((nr = read(from_fd, buf, sizeof(buf))) > 0)
|
|
if ((nw = write(to_fd, buf, nr)) != nr) {
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = nw > 0 ? EIO : serrno;
|
|
err(EX_OSERR, "%s", to_name);
|
|
}
|
|
if (nr != 0) {
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = serrno;
|
|
err(EX_OSERR, "%s", from_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* strip --
|
|
* use strip(1) to strip the target file
|
|
*/
|
|
void
|
|
strip(to_name)
|
|
char *to_name;
|
|
{
|
|
int serrno, status;
|
|
|
|
switch (vfork()) {
|
|
case -1:
|
|
serrno = errno;
|
|
(void)unlink(to_name);
|
|
errno = serrno;
|
|
err(EX_TEMPFAIL, "fork");
|
|
case 0:
|
|
execlp("strip", "strip", to_name, NULL);
|
|
err(EX_OSERR, "exec(strip)");
|
|
default:
|
|
if (wait(&status) == -1 || status) {
|
|
(void)unlink(to_name);
|
|
exit(EX_SOFTWARE);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* usage --
|
|
* print a usage message and die
|
|
*/
|
|
void
|
|
usage()
|
|
{
|
|
(void)fprintf(stderr,
|
|
"usage: install [-Ccdps] [-f flags] [-g group] [-m mode] [-o owner] file1 file2;\n\tor file1 ... fileN directory\n");
|
|
exit(1);
|
|
}
|
|
|
|
/*
|
|
* trymmap --
|
|
* return true (1) if mmap should be tried, false (0) if not.
|
|
*/
|
|
int
|
|
trymmap(fd)
|
|
int fd;
|
|
{
|
|
struct statfs stfs;
|
|
|
|
if (fstatfs(fd, &stfs) < 0)
|
|
return 0;
|
|
switch(stfs.f_type) {
|
|
case MOUNT_UFS: /* should be safe.. */
|
|
case MOUNT_CD9660: /* should be safe.. */
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|