xref: /illumos-gate/usr/src/lib/libcmdutils/common/writefile.c (revision ca9327a6de44d69ddab3668cc1e143ce781387a3)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
22 /*	  All Rights Reserved  	*/
23 
24 /*
25  * University Copyright- Copyright (c) 1982, 1986, 1988
26  * The Regents of the University of California
27  * All Rights Reserved
28  *
29  * University Acknowledgment- Portions of this document are derived from
30  * software developed by the University of California, Berkeley, and its
31  * contributors.
32  */
33 /*
34  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
35  * Use is subject to license terms.
36  */
37 
38 #pragma ident	"%Z%%M%	%I%	%E% SMI"
39 
40 #include <c_synonyms.h>
41 #include "libcmdutils.h"
42 
43 
44 int
45 writefile(int fi, int fo, char *infile, char *outfile, char *asfile,
46     char *atfile, struct stat *s1p, struct stat *s2p)
47 {
48 	int mapsize, munmapsize;
49 	caddr_t cp;
50 	off_t filesize = s1p->st_size;
51 	off_t offset;
52 	int nbytes;
53 	int remains;
54 	int n;
55 	size_t src_size;
56 	size_t targ_size;
57 	char *srcbuf;
58 	char *targbuf;
59 
60 	if (asfile != NULL) {
61 		src_size = strlen(infile) + strlen(asfile) +
62 		    strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
63 	} else {
64 		src_size = strlen(infile) + 1;
65 	}
66 	srcbuf = malloc(src_size);
67 	if (srcbuf == NULL) {
68 		(void) fprintf(stderr,
69 		    dgettext(TEXT_DOMAIN, "could not allocate memory"
70 		    " for path buffer: "));
71 		return (1);
72 	}
73 	if (asfile != NULL) {
74 		(void) snprintf(srcbuf, src_size, "%s%s%s",
75 		    infile, dgettext(TEXT_DOMAIN, " attribute "), asfile);
76 	} else {
77 		(void) snprintf(srcbuf, src_size, "%s", infile);
78 	}
79 
80 	if (atfile != NULL) {
81 		targ_size = strlen(outfile) + strlen(atfile) +
82 		    strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
83 	} else {
84 		targ_size = strlen(outfile) + 1;
85 	}
86 	targbuf = malloc(targ_size);
87 	if (targbuf == NULL) {
88 		(void) fprintf(stderr,
89 		    dgettext(TEXT_DOMAIN, "could not allocate memory"
90 		    " for path buffer: "));
91 		return (1);
92 	}
93 	if (atfile != NULL) {
94 		(void) snprintf(targbuf, targ_size, "%s%s%s",
95 		    outfile, dgettext(TEXT_DOMAIN, " attribute "), atfile);
96 	} else {
97 		(void) snprintf(targbuf, targ_size, "%s", outfile);
98 	}
99 
100 	if (ISREG(*s1p) && s1p->st_size > SMALLFILESIZE) {
101 		/*
102 		 * Determine size of initial mapping.  This will determine the
103 		 * size of the address space chunk we work with.  This initial
104 		 * mapping size will be used to perform munmap() in the future.
105 		 */
106 		mapsize = MAXMAPSIZE;
107 		if (s1p->st_size < mapsize) mapsize = s1p->st_size;
108 		munmapsize = mapsize;
109 
110 		/*
111 		 * Mmap time!
112 		 */
113 		if ((cp = mmap((caddr_t)NULL, mapsize, PROT_READ,
114 		    MAP_SHARED, fi, (off_t)0)) == MAP_FAILED)
115 			mapsize = 0;   /* can't mmap today */
116 	} else
117 		mapsize = 0;
118 
119 	if (mapsize != 0) {
120 		offset = 0;
121 
122 		for (;;) {
123 			nbytes = write(fo, cp, mapsize);
124 			/*
125 			 * if we write less than the mmaped size it's due to a
126 			 * media error on the input file or out of space on
127 			 * the output file.  So, try again, and look for errno.
128 			 */
129 			if ((nbytes >= 0) && (nbytes != (int)mapsize)) {
130 				remains = mapsize - nbytes;
131 				while (remains > 0) {
132 					nbytes = write(fo,
133 					    cp + mapsize - remains, remains);
134 					if (nbytes < 0) {
135 						if (errno == ENOSPC)
136 							perror(targbuf);
137 						else
138 							perror(srcbuf);
139 						(void) close(fi);
140 						(void) close(fo);
141 						(void) munmap(cp, munmapsize);
142 						if (ISREG(*s2p))
143 							(void) unlink(targbuf);
144 						return (1);
145 					}
146 					remains -= nbytes;
147 					if (remains == 0)
148 						nbytes = mapsize;
149 				}
150 			}
151 			/*
152 			 * although the write manual page doesn't specify this
153 			 * as a possible errno, it is set when the nfs read
154 			 * via the mmap'ed file is accessed, so report the
155 			 * problem as a source access problem, not a target file
156 			 * problem
157 			 */
158 			if (nbytes < 0) {
159 				if (errno == EACCES)
160 					perror(srcbuf);
161 				else
162 					perror(targbuf);
163 				(void) close(fi);
164 				(void) close(fo);
165 				(void) munmap(cp, munmapsize);
166 				if (ISREG(*s2p))
167 					(void) unlink(targbuf);
168 				if (srcbuf != NULL)
169 					free(srcbuf);
170 				if (targbuf != NULL)
171 					free(targbuf);
172 				return (1);
173 			}
174 			filesize -= nbytes;
175 			if (filesize == 0)
176 				break;
177 			offset += nbytes;
178 			if (filesize < mapsize)
179 				mapsize = filesize;
180 			if (mmap(cp, mapsize, PROT_READ, MAP_SHARED |
181 			    MAP_FIXED, fi, offset) == MAP_FAILED) {
182 				perror(srcbuf);
183 				(void) close(fi);
184 				(void) close(fo);
185 				(void) munmap(cp, munmapsize);
186 				if (ISREG(*s2p))
187 					(void) unlink(targbuf);
188 				if (srcbuf != NULL)
189 					free(srcbuf);
190 				if (targbuf != NULL)
191 					free(targbuf);
192 				return (1);
193 			}
194 		}
195 		(void) munmap(cp, munmapsize);
196 	} else {
197 		char buf[SMALLFILESIZE];
198 		for (;;) {
199 			n = read(fi, buf, sizeof (buf));
200 			if (n == 0) {
201 				return (0);
202 			} else if (n < 0) {
203 				(void) close(fi);
204 				(void) close(fo);
205 				if (ISREG(*s2p))
206 					(void) unlink(targbuf);
207 				if (srcbuf != NULL)
208 					free(srcbuf);
209 				if (targbuf != NULL)
210 					free(targbuf);
211 				return (1);
212 			} else if (write(fo, buf, n) != n) {
213 				(void) close(fi);
214 				(void) close(fo);
215 				if (ISREG(*s2p))
216 					(void) unlink(targbuf);
217 				if (srcbuf != NULL)
218 					free(srcbuf);
219 				if (targbuf != NULL)
220 					free(targbuf);
221 				return (1);
222 			}
223 		}
224 	}
225 	if (srcbuf != NULL)
226 		free(srcbuf);
227 	if (targbuf != NULL)
228 		free(targbuf);
229 	return (0);
230 }
231