xref: /illumos-gate/usr/src/lib/libcmdutils/common/writefile.c (revision 608eb926e14f4ba4736b2d59e891335f1cba9e1e)
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 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
28 /*	  All Rights Reserved	*/
29 
30 /*
31  * University Copyright- Copyright (c) 1982, 1986, 1988
32  * The Regents of the University of California
33  * All Rights Reserved
34  *
35  * University Acknowledgment- Portions of this document are derived from
36  * software developed by the University of California, Berkeley, and its
37  * contributors.
38  */
39 
40 /*
41  * Copyright 2024-2025 RackTop Systems, Inc.
42  */
43 
44 #include "libcmdutils.h"
45 
46 /*
47  * Helper for mv/cp/ln (see $SRC/cmd/mv)
48  * Copy a file "in" to "out" where
49  *	fi is a file descriptor open for read on "in"
50  *	fo is a file descriptor open for write on "out"
51  *	infile is a name used for error messages re. "in"
52  *	outfile is a name used for error messages re. "out"
53  *	asfile is an attr. name on "in" if copying an attr, else NULL
54  *	atfile is an attr. name on "out" if copying an attr, else NULL
55  *	s1p points to stat info for "in"
56  *	s2p points to stat info for "out"
57  *
58  * Returns 0 on success.  Returns 1 on failure AND closes fi, fo!
59  *
60  * Closing fi, fo on error may be surprising as an interface design
61  * but callers expect this behavior.
62  */
63 int
64 writefile(int fi, int fo, const char *infile, const char *outfile,
65     const char *asfile, const char *atfile, struct stat *s1p, struct stat *s2p)
66 {
67 	int mapsize, munmapsize;
68 	caddr_t cp;
69 	off_t filesize = s1p->st_size;
70 	off_t offset;
71 	int nbytes;
72 	int remains;
73 	int n;
74 	size_t src_size;
75 	size_t targ_size;
76 	char *srcbuf;
77 	char *targbuf;
78 
79 	if (asfile != NULL) {
80 		src_size = strlen(infile) + strlen(asfile) +
81 		    strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
82 	} else {
83 		src_size = strlen(infile) + 1;
84 	}
85 	srcbuf = malloc(src_size);
86 	if (srcbuf == NULL) {
87 		(void) fprintf(stderr,
88 		    dgettext(TEXT_DOMAIN, "could not allocate memory"
89 		    " for path buffer\n"));
90 		(void) close(fi);
91 		(void) close(fo);
92 		return (1);
93 	}
94 	if (asfile != NULL) {
95 		(void) snprintf(srcbuf, src_size, "%s%s%s",
96 		    infile, dgettext(TEXT_DOMAIN, " attribute "), asfile);
97 	} else {
98 		(void) snprintf(srcbuf, src_size, "%s", infile);
99 	}
100 
101 	if (atfile != NULL) {
102 		targ_size = strlen(outfile) + strlen(atfile) +
103 		    strlen(dgettext(TEXT_DOMAIN, " attribute ")) + 1;
104 	} else {
105 		targ_size = strlen(outfile) + 1;
106 	}
107 	targbuf = malloc(targ_size);
108 	if (targbuf == NULL) {
109 		(void) fprintf(stderr,
110 		    dgettext(TEXT_DOMAIN, "could not allocate memory"
111 		    " for path buffer\n"));
112 		free(srcbuf);
113 		(void) close(fi);
114 		(void) close(fo);
115 		return (1);
116 	}
117 	if (atfile != NULL) {
118 		(void) snprintf(targbuf, targ_size, "%s%s%s",
119 		    outfile, dgettext(TEXT_DOMAIN, " attribute "), atfile);
120 	} else {
121 		(void) snprintf(targbuf, targ_size, "%s", outfile);
122 	}
123 
124 	if (S_ISREG(s1p->st_mode) && s1p->st_size > SMALLFILESIZE) {
125 		/*
126 		 * Determine size of initial mapping.  This will determine the
127 		 * size of the address space chunk we work with.  This initial
128 		 * mapping size will be used to perform munmap() in the future.
129 		 */
130 		mapsize = MAXMAPSIZE;
131 		if (s1p->st_size < mapsize) mapsize = s1p->st_size;
132 		munmapsize = mapsize;
133 
134 		/*
135 		 * Mmap time!
136 		 */
137 		if ((cp = mmap((caddr_t)NULL, mapsize, PROT_READ,
138 		    MAP_SHARED, fi, (off_t)0)) == MAP_FAILED)
139 			mapsize = 0;   /* can't mmap today */
140 	} else
141 		mapsize = 0;
142 
143 	if (mapsize != 0) {
144 		offset = 0;
145 
146 		for (;;) {
147 			nbytes = write(fo, cp, mapsize);
148 			/*
149 			 * if we write less than the mmaped size it's due to a
150 			 * media error on the input file or out of space on
151 			 * the output file.  So, try again, and look for errno.
152 			 */
153 			if ((nbytes >= 0) && (nbytes != (int)mapsize)) {
154 				remains = mapsize - nbytes;
155 				while (remains > 0) {
156 					nbytes = write(fo,
157 					    cp + mapsize - remains, remains);
158 					if (nbytes < 0) {
159 						if (errno == ENOSPC)
160 							perror(targbuf);
161 						else
162 							perror(srcbuf);
163 						(void) close(fi);
164 						(void) close(fo);
165 						(void) munmap(cp, munmapsize);
166 						if (S_ISREG(s2p->st_mode))
167 							(void) unlink(targbuf);
168 						free(srcbuf);
169 						free(targbuf);
170 						return (1);
171 					}
172 					remains -= nbytes;
173 					if (remains == 0)
174 						nbytes = mapsize;
175 				}
176 			}
177 			/*
178 			 * although the write manual page doesn't specify this
179 			 * as a possible errno, it is set when the nfs read
180 			 * via the mmap'ed file is accessed, so report the
181 			 * problem as a source access problem, not a target file
182 			 * problem
183 			 */
184 			if (nbytes < 0) {
185 				if (errno == EACCES)
186 					perror(srcbuf);
187 				else
188 					perror(targbuf);
189 				(void) close(fi);
190 				(void) close(fo);
191 				(void) munmap(cp, munmapsize);
192 				if (S_ISREG(s2p->st_mode))
193 					(void) unlink(targbuf);
194 				free(srcbuf);
195 				free(targbuf);
196 				return (1);
197 			}
198 			filesize -= nbytes;
199 			if (filesize == 0)
200 				break;
201 			offset += nbytes;
202 			if (filesize < mapsize)
203 				mapsize = filesize;
204 			if (mmap(cp, mapsize, PROT_READ, MAP_SHARED |
205 			    MAP_FIXED, fi, offset) == MAP_FAILED) {
206 				perror(srcbuf);
207 				(void) close(fi);
208 				(void) close(fo);
209 				(void) munmap(cp, munmapsize);
210 				if (S_ISREG(s2p->st_mode))
211 					(void) unlink(targbuf);
212 				free(srcbuf);
213 				free(targbuf);
214 				return (1);
215 			}
216 		}
217 		(void) munmap(cp, munmapsize);
218 	} else {
219 		char buf[SMALLFILESIZE];
220 		for (;;) {
221 			n = read(fi, buf, sizeof (buf));
222 			if (n == 0) {
223 				free(srcbuf);
224 				free(targbuf);
225 				return (0);
226 			} else if (n < 0) {
227 				(void) close(fi);
228 				(void) close(fo);
229 				if (S_ISREG(s2p->st_mode))
230 					(void) unlink(targbuf);
231 				free(srcbuf);
232 				free(targbuf);
233 				return (1);
234 			} else if (write(fo, buf, n) != n) {
235 				(void) close(fi);
236 				(void) close(fo);
237 				if (S_ISREG(s2p->st_mode))
238 					(void) unlink(targbuf);
239 				free(srcbuf);
240 				free(targbuf);
241 				return (1);
242 			}
243 		}
244 	}
245 	free(srcbuf);
246 	free(targbuf);
247 	return (0);
248 }
249