xref: /libuv/test/test-fs-copyfile.c (revision 8a499e13)
1 /* Copyright libuv project contributors. All rights reserved.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining a copy
4  * of this software and associated documentation files (the "Software"), to
5  * deal in the Software without restriction, including without limitation the
6  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7  * sell copies of the Software, and to permit persons to whom the Software is
8  * furnished to do so, subject to the following conditions:
9  *
10  * The above copyright notice and this permission notice shall be included in
11  * all copies or substantial portions of the Software.
12  *
13  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19  * IN THE SOFTWARE.
20  */
21 
22 #include "uv.h"
23 #include "task.h"
24 
25 #if defined(__unix__) || defined(__POSIX__) || \
26     defined(__APPLE__) || defined(__sun) || \
27     defined(_AIX) || defined(__MVS__) || \
28     defined(__HAIKU__) || defined(__QNX__)
29 #include <unistd.h> /* unlink, etc. */
30 #else
31 # include <direct.h>
32 # include <io.h>
33 # define unlink _unlink
34 #endif
35 
36 static const char fixture[] = "test/fixtures/load_error.node";
37 static const char dst[] = "test_file_dst";
38 static int result_check_count;
39 
40 
fail_cb(uv_fs_t * req)41 static void fail_cb(uv_fs_t* req) {
42   FATAL("fail_cb should not have been called");
43 }
44 
handle_result(uv_fs_t * req)45 static void handle_result(uv_fs_t* req) {
46   uv_fs_t stat_req;
47   uint64_t size;
48   uint64_t mode;
49   int r;
50 
51   ASSERT_EQ(req->fs_type, UV_FS_COPYFILE);
52   ASSERT_OK(req->result);
53 
54   /* Verify that the file size and mode are the same. */
55   r = uv_fs_stat(NULL, &stat_req, req->path, NULL);
56   ASSERT_OK(r);
57   size = stat_req.statbuf.st_size;
58   mode = stat_req.statbuf.st_mode;
59   uv_fs_req_cleanup(&stat_req);
60   r = uv_fs_stat(NULL, &stat_req, dst, NULL);
61   ASSERT_OK(r);
62   ASSERT_EQ(stat_req.statbuf.st_size, size);
63   ASSERT_EQ(stat_req.statbuf.st_mode, mode);
64   uv_fs_req_cleanup(&stat_req);
65   uv_fs_req_cleanup(req);
66   result_check_count++;
67 }
68 
69 
touch_file(const char * name,unsigned int size)70 static void touch_file(const char* name, unsigned int size) {
71   uv_file file;
72   uv_fs_t req;
73   uv_buf_t buf;
74   int r;
75   unsigned int i;
76 
77   r = uv_fs_open(NULL, &req, name,
78                  UV_FS_O_WRONLY | UV_FS_O_CREAT | UV_FS_O_TRUNC,
79                  S_IWUSR | S_IRUSR, NULL);
80   uv_fs_req_cleanup(&req);
81   ASSERT_GE(r, 0);
82   file = r;
83 
84   buf = uv_buf_init("a", 1);
85 
86   /* Inefficient but simple. */
87   for (i = 0; i < size; i++) {
88     r = uv_fs_write(NULL, &req, file, &buf, 1, i, NULL);
89     uv_fs_req_cleanup(&req);
90     ASSERT_GE(r, 0);
91   }
92 
93   r = uv_fs_close(NULL, &req, file, NULL);
94   uv_fs_req_cleanup(&req);
95   ASSERT_OK(r);
96 }
97 
98 
TEST_IMPL(fs_copyfile)99 TEST_IMPL(fs_copyfile) {
100   const char src[] = "test_file_src";
101   uv_loop_t* loop;
102   uv_fs_t req;
103   int r;
104 
105   loop = uv_default_loop();
106 
107   /* Fails with EINVAL if bad flags are passed. */
108   r = uv_fs_copyfile(NULL, &req, src, dst, -1, NULL);
109   ASSERT_EQ(r, UV_EINVAL);
110   uv_fs_req_cleanup(&req);
111 
112   /* Fails with ENOENT if source does not exist. */
113   unlink(src);
114   unlink(dst);
115   r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL);
116   ASSERT_EQ(req.result, UV_ENOENT);
117   ASSERT_EQ(r, UV_ENOENT);
118   uv_fs_req_cleanup(&req);
119   /* The destination should not exist. */
120   r = uv_fs_stat(NULL, &req, dst, NULL);
121   ASSERT(r);
122   uv_fs_req_cleanup(&req);
123 
124   /* Succeeds if src and dst files are identical. */
125   touch_file(src, 12);
126   r = uv_fs_copyfile(NULL, &req, src, src, 0, NULL);
127   ASSERT_OK(r);
128   uv_fs_req_cleanup(&req);
129   /* Verify that the src file did not get truncated. */
130   r = uv_fs_stat(NULL, &req, src, NULL);
131   ASSERT_OK(r);
132   ASSERT_EQ(12, req.statbuf.st_size);
133   uv_fs_req_cleanup(&req);
134   unlink(src);
135 
136   /* Copies file synchronously. Creates new file. */
137   unlink(dst);
138   r = uv_fs_copyfile(NULL, &req, fixture, dst, 0, NULL);
139   ASSERT_OK(r);
140   handle_result(&req);
141 
142   /* Copies a file of size zero. */
143   unlink(dst);
144   touch_file(src, 0);
145   r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL);
146   ASSERT_OK(r);
147   handle_result(&req);
148 
149   /* Copies file synchronously. Overwrites existing file. */
150   r = uv_fs_copyfile(NULL, &req, fixture, dst, 0, NULL);
151   ASSERT_OK(r);
152   handle_result(&req);
153 
154   /* Fails to overwrites existing file. */
155   ASSERT_OK(uv_fs_chmod(NULL, &req, dst, 0644, NULL));
156   uv_fs_req_cleanup(&req);
157   r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_EXCL, NULL);
158   ASSERT_EQ(r, UV_EEXIST);
159   uv_fs_req_cleanup(&req);
160 
161   /* Truncates when an existing destination is larger than the source file. */
162   ASSERT_OK(uv_fs_chmod(NULL, &req, dst, 0644, NULL));
163   uv_fs_req_cleanup(&req);
164   touch_file(src, 1);
165   r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL);
166   ASSERT_OK(r);
167   handle_result(&req);
168 
169   /* Copies a larger file. */
170   unlink(dst);
171   touch_file(src, 4096 * 2);
172   r = uv_fs_copyfile(NULL, &req, src, dst, 0, NULL);
173   ASSERT_OK(r);
174   handle_result(&req);
175   unlink(src);
176 
177   /* Copies file asynchronously */
178   unlink(dst);
179   r = uv_fs_copyfile(loop, &req, fixture, dst, 0, handle_result);
180   ASSERT_OK(r);
181   ASSERT_EQ(5, result_check_count);
182   uv_run(loop, UV_RUN_DEFAULT);
183   ASSERT_EQ(6, result_check_count);
184   /* Ensure file is user-writable (not copied from src). */
185   ASSERT_OK(uv_fs_chmod(NULL, &req, dst, 0644, NULL));
186   uv_fs_req_cleanup(&req);
187 
188   /* If the flags are invalid, the loop should not be kept open */
189   unlink(dst);
190   r = uv_fs_copyfile(loop, &req, fixture, dst, -1, fail_cb);
191   ASSERT_EQ(r, UV_EINVAL);
192   uv_run(loop, UV_RUN_DEFAULT);
193 
194   /* Copies file using UV_FS_COPYFILE_FICLONE. */
195   unlink(dst);
196   r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_FICLONE, NULL);
197   ASSERT_OK(r);
198   handle_result(&req);
199 
200   /* Copies file using UV_FS_COPYFILE_FICLONE_FORCE. */
201   unlink(dst);
202   r = uv_fs_copyfile(NULL, &req, fixture, dst, UV_FS_COPYFILE_FICLONE_FORCE,
203                      NULL);
204   ASSERT_LE(r, 0);
205 
206   if (r == 0)
207     handle_result(&req);
208 
209 #ifndef _WIN32
210   /* Copying respects permissions/mode. */
211   unlink(dst);
212   touch_file(dst, 0);
213   chmod(dst, S_IRUSR|S_IRGRP|S_IROTH); /* Sets file mode to 444 (read-only). */
214   r = uv_fs_copyfile(NULL, &req, fixture, dst, 0, NULL);
215   /* On IBMi PASE, qsecofr users can overwrite read-only files */
216 # ifndef __PASE__
217   ASSERT_EQ(req.result, UV_EACCES);
218   ASSERT_EQ(r, UV_EACCES);
219 # endif
220   uv_fs_req_cleanup(&req);
221 #endif
222 
223   unlink(dst); /* Cleanup */
224   MAKE_VALGRIND_HAPPY(loop);
225   return 0;
226 }
227