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
writefile(int fi,int fo,const char * infile,const char * outfile,const char * asfile,const char * atfile,struct stat * s1p,struct stat * s2p)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