This source file includes following definitions.
- set_op_not_supported
- windows_safe_rename_noreplace
- x_unlink
- x_link
- x_read
- x_write
- set_mode_owner_times
- try_linux_rename_noreplace
- copy_noreplace_then_unlink
- link_then_unlink_move
- posix_safe_rename_noreplace
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 #if !defined(SAFEMOVE_H)
20 # define SAFEMOVE_H
21
22
23
24 # if !defined(_POSIX_C_SOURCE)
25 # define _POSIX_C_SOURCE 200809L
26 # endif
27
28 # if !defined(_GNU_SOURCE)
29 # define _GNU_SOURCE
30 # endif
31
32 # if !defined(_NETBSD_SOURCE)
33 # define _NETBSD_SOURCE
34 # endif
35
36 # if !defined(_OPENBSD_SOURCE)
37 # define _OPENBSD_SOURCE
38 # endif
39
40
41
42 # include <errno.h>
43 # include <fcntl.h>
44 # include <limits.h>
45 # include <stdarg.h>
46 # include <stdbool.h>
47 # include <stddef.h>
48 # include <stdint.h>
49 # include <stdio.h>
50 # include <stdlib.h>
51 # include <string.h>
52 # include <sys/stat.h>
53 # include <sys/types.h>
54 # include <unistd.h>
55 # include <utime.h>
56
57
58
59 # undef HAS_INCLUDE
60 # if defined __has_include
61 # define HAS_INCLUDE(inc) __has_include(inc)
62 # else
63 # define HAS_INCLUDE(inc) 0
64 # endif
65
66
67
68 # if defined(__linux__)
69 # if HAS_INCLUDE(<linux/fs.h>)
70 # include <linux/fs.h>
71 # endif
72 # if HAS_INCLUDE(<sys/syscall.h>)
73 # include <sys/syscall.h>
74 # endif
75 # endif
76
77
78
79 # if defined(_WIN32)
80 # include <windows.h>
81 # include <fileapi.h>
82 # endif
83
84
85
86 # if defined(FREE)
87 # undef FREE
88 # endif
89 # define FREE(p) do \
90 { \
91 free ((p)); \
92 (p) = NULL; \
93 } \
94 while (0)
95
96
97
98 static void
99 set_op_not_supported (void)
100 {
101 # if defined(ENOTSUP)
102 errno = ENOTSUP;
103 # elif defined(EOPNOTSUPP)
104 errno = EOPNOTSUPP;
105 # else
106 errno = ENOSYS;
107 # endif
108 }
109
110
111
112 # if defined(_WIN32)
113 static int
114 windows_safe_rename_noreplace (const char * src, const char * dst)
115 {
116 if (! src || ! dst)
117 {
118 errno = EINVAL;
119 return -1;
120 }
121
122 wchar_t wsrc [MAX_PATH], wdst [MAX_PATH];
123 if (MultiByteToWideChar (CP_UTF8, 0, src, -1, wsrc, MAX_PATH) == 0 ||
124 MultiByteToWideChar (CP_UTF8, 0, dst, -1, wdst, MAX_PATH) == 0)
125 {
126 errno = EINVAL;
127 return -1;
128 }
129
130 DWORD attr = GetFileAttributesW (wsrc);
131 if (attr == INVALID_FILE_ATTRIBUTES)
132 {
133 errno = ENOENT;
134 return -1;
135 }
136
137 if (attr & FILE_ATTRIBUTE_REPARSE_POINT)
138 {
139 set_op_not_supported ();
140 return -1;
141 }
142
143 if (_stricmp (src, dst) == 0)
144 return 0;
145
146 if (MoveFileExW (wsrc, wdst, MOVEFILE_WRITE_THROUGH))
147 return 0;
148
149 DWORD err = GetLastError ();
150 if (err == ERROR_FILE_EXISTS || err == ERROR_ALREADY_EXISTS)
151 {
152 errno = EEXIST;
153 return -1;
154 }
155
156 if (err == ERROR_NOT_SAME_DEVICE)
157 {
158 DWORD sattr = GetFileAttributesW (wsrc);
159
160 if (sattr == INVALID_FILE_ATTRIBUTES)
161 {
162 errno = ENOENT;
163 return -1;
164 }
165
166 if (sattr & FILE_ATTRIBUTE_DIRECTORY)
167 {
168 errno = EISDIR;
169 return -1;
170 }
171
172 if (! CopyFileW (wsrc, wdst, TRUE))
173 {
174 DWORD cerr = GetLastError ();
175
176 if (cerr == ERROR_FILE_EXISTS || cerr == ERROR_ALREADY_EXISTS)
177 errno = EEXIST;
178 else
179 errno = EIO;
180
181 return -1;
182 }
183
184 if (! DeleteFileW (wsrc))
185 {
186 DeleteFileW (wdst);
187 errno = EIO;
188 return -1;
189 }
190
191 return 0;
192 }
193
194 errno = EIO;
195
196 return -1;
197 }
198
199
200
201 # define safe_rename_noreplace(src, dst) windows_safe_rename_noreplace(src, dst)
202 # else
203
204
205
206 static int
207 x_unlink (const char * path)
208 {
209 int rc;
210 do
211 rc = unlink (path);
212 while (rc == -1 && errno == EINTR);
213
214 return rc;
215 }
216
217
218
219 static int
220 x_link (const char * oldp, const char * newp)
221 {
222 int rc;
223 do
224 rc = link (oldp, newp);
225 while (rc == -1 && errno == EINTR);
226
227 return rc;
228 }
229
230
231
232 static ssize_t
233 x_read (int fd, void * buf, size_t sz)
234 {
235 for (;;)
236 {
237 ssize_t n = read (fd, buf, sz);
238
239 if (n >= 0)
240 return n;
241
242 if (errno != EINTR)
243 return -1;
244 }
245 }
246
247
248
249 static ssize_t
250 x_write (int fd, const void * buf, size_t sz)
251 {
252 const unsigned char * p = (const unsigned char *)buf;
253 size_t left = sz;
254
255 while (left)
256 {
257 ssize_t n = write (fd, p, left);
258
259 if (n < 0)
260 {
261 if (errno == EINTR)
262 continue;
263
264 return -1;
265 }
266
267 p += n;
268 left -= (size_t)n;
269 }
270
271 return (ssize_t)sz;
272 }
273
274
275
276 static int
277 set_mode_owner_times (const char * dst_path, int dst_fd, const struct stat * st)
278 {
279 if (fchown (dst_fd, st -> st_uid, st -> st_gid) == -1 && errno != EPERM)
280 return -1;
281
282 if (fchmod (dst_fd, st -> st_mode & 07777) == -1)
283 return -1;
284
285 struct utimbuf ut =
286 {
287 . actime = st -> st_atime,
288 . modtime = st -> st_mtime
289 };
290
291 if (utime (dst_path, & ut) == -1)
292 return -1;
293
294 return 0;
295 }
296
297
298
299 static int
300 try_linux_rename_noreplace (const char * src, const char * dst, int * handled)
301 {
302 * handled = 0;
303 # if defined(__linux__) && defined(AT_FDCWD) && defined(RENAME_NOREPLACE)
304 # if defined(SYS_renameat2)
305 int rc = (int)syscall (SYS_renameat2, AT_FDCWD, src, AT_FDCWD, dst, RENAME_NOREPLACE);
306
307 if (rc == 0)
308 {
309 * handled = 1;
310 return 0;
311 }
312
313 if (rc == -1)
314 {
315 if (errno == ENOSYS || errno == EINVAL)
316 {
317 * handled = 0;
318 errno = 0;
319 return -1;
320 }
321
322 if (errno == EXDEV)
323 {
324 * handled = 1;
325 return -1;
326 }
327
328 * handled = 1;
329
330 return -1;
331 }
332 # else
333 (void)src;
334 (void)dst;
335 # endif
336 # endif
337 return -1;
338 }
339
340
341
342 static int
343 copy_noreplace_then_unlink (const char * src, const char * dst, const struct stat * src_st)
344 {
345 int sfd = -1, dfd = -1;
346 int rc = -1;
347
348 sfd = open (src, O_RDONLY);
349
350 if (sfd == -1)
351 goto safemove_out;
352
353 struct stat lst;
354
355 if (src_st)
356 lst = * src_st;
357 else if (fstat (sfd, & lst) == -1)
358 goto safemove_out;
359
360 if (! S_ISREG (lst . st_mode))
361 {
362 errno = EXDEV;
363 goto safemove_out;
364 }
365
366 dfd = open (dst, O_WRONLY | O_CREAT | O_EXCL, 0600);
367
368 if (dfd == -1)
369 goto safemove_out;
370
371 const size_t BUFSZ = 1 << 20;
372
373 void * buf = malloc (BUFSZ);
374 if (! buf)
375 {
376 errno = ENOMEM;
377 goto safemove_out;
378 }
379
380 for (;;)
381 {
382 ssize_t n = x_read (sfd, buf, BUFSZ);
383
384 if (n < 0)
385 {
386 FREE (buf);
387 goto safemove_out;
388 }
389
390 if (n == 0)
391 {
392 FREE (buf);
393 break;
394 }
395
396 if (x_write (dfd, buf, (size_t)n) < 0)
397 {
398 FREE (buf);
399 goto safemove_out;
400 }
401 }
402
403 (void)fsync (dfd);
404
405 if (set_mode_owner_times (dst, dfd, & lst) == -1)
406 goto safemove_out;
407
408 if (close (dfd) == -1)
409 {
410 dfd = -1;
411 goto safemove_out;
412 }
413
414 dfd = -1;
415
416 if (x_unlink (src) == -1)
417 {
418 int e = errno;
419 (void)x_unlink (dst);
420 errno = e;
421 goto safemove_out;
422 }
423
424 rc = 0;
425
426 safemove_out:
427 {
428 int saved = errno;
429
430 if (sfd != -1)
431 (void)close (sfd);
432
433 if (dfd != -1)
434 {
435 int e2 = errno;
436 (void)close (dfd);
437 errno = e2;
438 }
439
440 errno = saved;
441 }
442
443 return rc;
444 }
445
446
447
448 static int
449 link_then_unlink_move (const char * src, const char * dst)
450 {
451 if (x_link (src, dst) == -1)
452 return -1;
453
454 if (x_unlink (src) == -1)
455 {
456 int e = errno;
457 (void)x_unlink (dst);
458 errno = e;
459 return -1;
460 }
461
462 return 0;
463 }
464
465
466
467 static int
468 posix_safe_rename_noreplace (const char * src, const char * dst)
469 {
470 if (! src || ! dst)
471 {
472 errno = EINVAL;
473 return -1;
474 }
475
476 struct stat st;
477 if (lstat (src, & st) == -1)
478 return -1;
479
480 if (S_ISLNK (st . st_mode))
481 {
482 set_op_not_supported ();
483 return -1;
484 }
485
486 if (strcmp (src, dst) == 0)
487 return 0;
488
489 int handled = 0;
490
491 if (try_linux_rename_noreplace (src, dst, & handled) == 0)
492 return 0;
493
494 if (handled && errno != EXDEV)
495 return -1;
496
497 if (S_ISDIR (st . st_mode))
498 {
499 errno = EISDIR;
500 return -1;
501 }
502
503 if (link_then_unlink_move (src, dst) == 0)
504 return 0;
505
506 if (errno == EEXIST)
507 return -1;
508
509 if (errno != EXDEV)
510 return -1;
511
512 if (copy_noreplace_then_unlink (src, dst, & st) == -1)
513 return -1;
514
515 return 0;
516 }
517
518
519
520 # define safe_rename_noreplace(src, dst) posix_safe_rename_noreplace(src, dst)
521 # endif
522
523
524
525 # define safe_rename(src, dst) safe_rename_noreplace(src, dst)
526
527
528
529 #endif
530
531