OpenSolaris_b135/lib/libcmdutils/common/writefile.c

Compare this file to the similar file:
Show the results in this format:

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */

/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/

/*
 * University Copyright- Copyright (c) 1982, 1986, 1988
 * The Regents of the University of California
 * All Rights Reserved
 *
 * University Acknowledgment- Portions of this document are derived from
 * software developed by the University of California, Berkeley, and its
 * contributors.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

#include "libcmdutils.h"


int
writefile(int fi, int fo, char *infile, char *outfile, char *asfile,
    char *atfile, struct stat *s1p, struct stat *s2p)
{
	int mapsize, munmapsize;
	caddr_t cp;
	off_t filesize = s1p->st_size;
	off_t offset;
	int nbytes;
	int remains;
	int n;
	size_t src_size;
	size_t targ_size;
	char *srcbuf;
	char *targbuf;

	if (asfile != NULL) {
		src_size = strlen(infile) + strlen(asfile) +
		    strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
	} else {
		src_size = strlen(infile) + 1;
	}
	srcbuf = malloc(src_size);
	if (srcbuf == NULL) {
		(void) fprintf(stderr,
		    dgettext(TEXT_DOMAIN, "could not allocate memory"
		    " for path buffer: "));
		return (1);
	}
	if (asfile != NULL) {
		(void) snprintf(srcbuf, src_size, "%s%s%s",
		    infile, dgettext(TEXT_DOMAIN, " attribute "), asfile);
	} else {
		(void) snprintf(srcbuf, src_size, "%s", infile);
	}

	if (atfile != NULL) {
		targ_size = strlen(outfile) + strlen(atfile) +
		    strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
	} else {
		targ_size = strlen(outfile) + 1;
	}
	targbuf = malloc(targ_size);
	if (targbuf == NULL) {
		(void) fprintf(stderr,
		    dgettext(TEXT_DOMAIN, "could not allocate memory"
		    " for path buffer: "));
		return (1);
	}
	if (atfile != NULL) {
		(void) snprintf(targbuf, targ_size, "%s%s%s",
		    outfile, dgettext(TEXT_DOMAIN, " attribute "), atfile);
	} else {
		(void) snprintf(targbuf, targ_size, "%s", outfile);
	}

	if (ISREG(*s1p) && s1p->st_size > SMALLFILESIZE) {
		/*
		 * Determine size of initial mapping.  This will determine the
		 * size of the address space chunk we work with.  This initial
		 * mapping size will be used to perform munmap() in the future.
		 */
		mapsize = MAXMAPSIZE;
		if (s1p->st_size < mapsize) mapsize = s1p->st_size;
		munmapsize = mapsize;

		/*
		 * Mmap time!
		 */
		if ((cp = mmap((caddr_t)NULL, mapsize, PROT_READ,
		    MAP_SHARED, fi, (off_t)0)) == MAP_FAILED)
			mapsize = 0;   /* can't mmap today */
	} else
		mapsize = 0;

	if (mapsize != 0) {
		offset = 0;

		for (;;) {
			nbytes = write(fo, cp, mapsize);
			/*
			 * if we write less than the mmaped size it's due to a
			 * media error on the input file or out of space on
			 * the output file.  So, try again, and look for errno.
			 */
			if ((nbytes >= 0) && (nbytes != (int)mapsize)) {
				remains = mapsize - nbytes;
				while (remains > 0) {
					nbytes = write(fo,
					    cp + mapsize - remains, remains);
					if (nbytes < 0) {
						if (errno == ENOSPC)
							perror(targbuf);
						else
							perror(srcbuf);
						(void) close(fi);
						(void) close(fo);
						(void) munmap(cp, munmapsize);
						if (ISREG(*s2p))
							(void) unlink(targbuf);
						return (1);
					}
					remains -= nbytes;
					if (remains == 0)
						nbytes = mapsize;
				}
			}
			/*
			 * although the write manual page doesn't specify this
			 * as a possible errno, it is set when the nfs read
			 * via the mmap'ed file is accessed, so report the
			 * problem as a source access problem, not a target file
			 * problem
			 */
			if (nbytes < 0) {
				if (errno == EACCES)
					perror(srcbuf);
				else
					perror(targbuf);
				(void) close(fi);
				(void) close(fo);
				(void) munmap(cp, munmapsize);
				if (ISREG(*s2p))
					(void) unlink(targbuf);
				if (srcbuf != NULL)
					free(srcbuf);
				if (targbuf != NULL)
					free(targbuf);
				return (1);
			}
			filesize -= nbytes;
			if (filesize == 0)
				break;
			offset += nbytes;
			if (filesize < mapsize)
				mapsize = filesize;
			if (mmap(cp, mapsize, PROT_READ, MAP_SHARED |
			    MAP_FIXED, fi, offset) == MAP_FAILED) {
				perror(srcbuf);
				(void) close(fi);
				(void) close(fo);
				(void) munmap(cp, munmapsize);
				if (ISREG(*s2p))
					(void) unlink(targbuf);
				if (srcbuf != NULL)
					free(srcbuf);
				if (targbuf != NULL)
					free(targbuf);
				return (1);
			}
		}
		(void) munmap(cp, munmapsize);
	} else {
		char buf[SMALLFILESIZE];
		for (;;) {
			n = read(fi, buf, sizeof (buf));
			if (n == 0) {
				return (0);
			} else if (n < 0) {
				(void) close(fi);
				(void) close(fo);
				if (ISREG(*s2p))
					(void) unlink(targbuf);
				if (srcbuf != NULL)
					free(srcbuf);
				if (targbuf != NULL)
					free(targbuf);
				return (1);
			} else if (write(fo, buf, n) != n) {
				(void) close(fi);
				(void) close(fo);
				if (ISREG(*s2p))
					(void) unlink(targbuf);
				if (srcbuf != NULL)
					free(srcbuf);
				if (targbuf != NULL)
					free(targbuf);
				return (1);
			}
		}
	}
	if (srcbuf != NULL)
		free(srcbuf);
	if (targbuf != NULL)
		free(targbuf);
	return (0);
}