libite 2.6.1
copyfile.c
Go to the documentation of this file.
1/* Fastinit (finit) copyfile() implementation.
2 *
3 * Copyright (c) 2008 Claudio Matsuoka <http://helllabs.org/finit/>
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and associated documentation files (the "Software"), to deal
7 * in the Software without restriction, including without limitation the rights
8 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 * copies of the Software, and to permit persons to whom the Software is
10 * furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 * THE SOFTWARE.
22 */
23
30
31#include <errno.h>
32#include <fcntl.h>
33#include <stdlib.h>
34#include "lite.h"
35
36/* Tests if dst is a directory, if so, reallocates dst and appends src filename returning 1 */
37static int adjust_target(const char *src, char **dst)
38{
39 int isdir = 0;
40
41 retry:
42 if (fisdir(*dst)) {
43 int slash = 0;
44 char *tmp, *ptr = strrchr(src, '/');
45
46 if (!ptr)
47 ptr = (char *)src;
48 else
49 ptr++;
50
51 tmp = malloc(strlen(*dst) + strlen(ptr) + 2);
52 if (!tmp) {
53 errno = EISDIR;
54 return 0;
55 }
56
57 isdir = 1; /* Free dst before exit! */
58 slash = fisslashdir(*dst);
59
60 sprintf(tmp, "%s%s%s", *dst, slash ? "" : "/", ptr);
61 *dst = tmp;
62 } else {
63 struct stat st;
64
65 if (stat(*dst, &st) && fisslashdir(*dst)) {
66 makedir(*dst, 0755);
67 goto retry;
68 }
69 }
70
71 return isdir;
72}
73
74/*
75 * Actual copy loop, used by both copyfile() and fcopyfile() breaks loop
76 * on error and EOF
77 */
78static ssize_t do_copy(int in, int out, size_t num, char *buffer, size_t len)
79{
80 ssize_t ret = 0, size = -1;
81
82 do {
83 size_t count = num > len ? len : num;
84
85 ret = read(in, buffer, count);
86 if (ret <= 0) {
87 if (ret == -1 && EINTR == errno)
88 continue;
89 if (ret == 0 && size == -1)
90 size = 0;
91 break;
92 }
93
94 if (ret > 0) {
95 if (size == -1)
96 size = 0;
97 size += write(out, buffer, ret);
98 }
99 num -= count;
100 } while (num > 0);
101
102 return size;
103}
104
105static void set_mtime(int in, int out)
106{
107 struct stat st;
108 struct timespec tv[2];
109
110 if (fstat(in, &st))
111 return;
112
113 tv[0] = st.st_atim;
114 tv[1] = st.st_mtim;
115 futimens(out, tv);
116}
117
144ssize_t copyfile(const char *src, const char *dst, int len, int opt)
145{
146 char *buffer, *dest = (char *)dst;
147 int sym = (opt & LITE_FOPT_COPYFILE_SYM) != 0;
148 int keep_mtim = (opt & LITE_FOPT_KEEP_MTIME) != 0;
149 int in, out, isdir = 0, saved_errno = 0;
150 size_t num;
151 ssize_t size = 0;
152 struct stat st;
153
154 errno = 0;
155
156 buffer = malloc(BUFSIZ);
157 if (!buffer)
158 return 0;
159
160 if (fisdir(src)) {
161 saved_errno = EISDIR; /* Error: source is a directory */
162 goto exit;
163 }
164
165 /* Check if target is a directory, then append src filename. */
166 isdir = adjust_target(src, &dest);
167
168 /* Check if the source file is a symlink ... */
169 if (stat(src, &st)) {
170 size = -1;
171 goto exit;
172 }
173
174 /* ... only try readlink() if symlink and @sym is set. */
175 if (sym && (st.st_mode & S_IFMT) == S_IFLNK) {
176 size = readlink(src, buffer, BUFSIZ);
177 if (size > 0) {
178 if (size >= (ssize_t)BUFSIZ) {
179 saved_errno = ENOBUFS;
180 size = -1;
181 } else {
182 buffer[size] = 0;
183 size = !symlink(buffer, dest);
184 }
185 }
186
187 /* Don't fall back to copy, that is a potentially
188 * dangerous race condition, see CWE-367. */
189 goto exit;
190 }
191
192 /* 0: copy entire file */
193 if (len == 0)
194 num = st.st_size;
195 else
196 num = (size_t)len;
197
198 in = open(src, O_RDONLY);
199 if (in < 0) {
200 saved_errno = errno;
201 goto exit;
202 }
203
204 out = open(dest, O_WRONLY | O_CREAT | O_TRUNC, st.st_mode);
205 if (out < 0) {
206 close(in);
207 saved_errno = errno;
208 goto exit;
209 }
210
211 size = do_copy(in, out, num, buffer, BUFSIZ);
212 if (size < 0)
213 saved_errno = errno;
214 else if (keep_mtim)
215 set_mtime(in, out);
216
217 close(out);
218 close(in);
219
220exit:
221 free(buffer);
222 if (isdir)
223 free(dest);
224 errno = saved_errno;
225
226 return size;
227}
228
244int movefile(const char *src, const char *dst)
245{
246 char *dest = (char *)dst;
247 int isdir, result = 0;
248
249 /* Check if target is a directory, then append the src base filename. */
250 isdir = adjust_target(src, &dest);
251
252 if (rename(src, dest)) {
253 if (errno == EXDEV) {
254 errno = 0;
255 copyfile(src, dest, 0, 1);
256 if (errno)
257 result = 1;
258 else
259 result = remove(src);
260 } else {
261 result = 1;
262 }
263 }
264
265 if (isdir)
266 free(dest);
267
268 return result;
269}
270
278int fcopyfile(FILE *src, FILE *dst)
279{
280 char *buf;
281
282 if (!src || !dst) {
283 errno = EINVAL;
284 return -1;
285 }
286
287 buf = malloc(BUFSIZ);
288 if (!buf)
289 return -1;
290
291 while (fgets(buf, BUFSIZ, src))
292 fputs(buf, dst);
293
294 free(buf);
295
296 return 0;
297}
298
int movefile(const char *src, const char *dst)
Definition copyfile.c:244
int fcopyfile(FILE *src, FILE *dst)
Definition copyfile.c:278
ssize_t copyfile(const char *src, const char *dst, int len, int opt)
Definition copyfile.c:144
int fisdir(const char *path)
Definition fisdir.c:40
#define LITE_FOPT_KEEP_MTIME
Definition lite.h:69
#define LITE_FOPT_COPYFILE_SYM
Definition lite.h:68