Mercurial > hg > octave-lojdl > gnulib-hg
annotate lib/rename.c @ 12103:832357ca223a
rename, fchdir: don't ignore chdir failure
Although we just checked that chdir(cwd) worked, there is a
race where it could disappear while we are temporarily away.
If that happens, forcefully give up rather than proceeding
in the wrong directory.
* lib/fchdir.c (get_name): Abort on unexpected chdir failure.
* lib/rename.c (rpl_rename) [W32]: Likewise.
(rpl_rename) [RENAME_DEST_EXISTS_BUG]: Avoid one case of losing
an empty destination directory if source cannot be renamed,
although there is still possibility for failure.
* doc/posix-functions/rename.texi (rename): Document the race.
Reported by Jim Meyering.
Signed-off-by: Eric Blake <ebb9@byu.net>
author | Eric Blake <ebb9@byu.net> |
---|---|
date | Fri, 02 Oct 2009 12:05:02 -0600 |
parents | 3ba227551e1d |
children | b5e42ef33b49 |
rev | line source |
---|---|
12093 | 1 /* Work around rename bugs in some systems. |
7302
8a1a9361108c
* _fpending.c: Include <config.h> unconditionally, since we no
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
2 |
11972 | 3 Copyright (C) 2001, 2002, 2003, 2005, 2006, 2009 Free Software |
4 Foundation, Inc. | |
3099 | 5 |
9309
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
6 This program is free software: you can redistribute it and/or modify |
3099 | 7 it under the terms of the GNU General Public License as published by |
9309
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
8 the Free Software Foundation; either version 3 of the License, or |
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
9 (at your option) any later version. |
3099 | 10 |
11 This program is distributed in the hope that it will be useful, | |
12 but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 GNU General Public License for more details. | |
15 | |
16 You should have received a copy of the GNU General Public License | |
9309
bbbbbf4cd1c5
Change copyright notice from GPLv2+ to GPLv3+.
Bruno Haible <bruno@clisp.org>
parents:
7302
diff
changeset
|
17 along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
3099 | 18 |
12093 | 19 /* Written by Volker Borchert, Eric Blake. */ |
3099 | 20 |
7302
8a1a9361108c
* _fpending.c: Include <config.h> unconditionally, since we no
Paul Eggert <eggert@cs.ucla.edu>
parents:
6259
diff
changeset
|
21 #include <config.h> |
11972 | 22 |
23 #include <stdio.h> | |
24 | |
4229
c7ddde35beec
Make this module usable in shared libraries.
Bruno Haible <bruno@clisp.org>
parents:
3782
diff
changeset
|
25 #undef rename |
c7ddde35beec
Make this module usable in shared libraries.
Bruno Haible <bruno@clisp.org>
parents:
3782
diff
changeset
|
26 |
11972 | 27 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ |
28 /* The mingw rename has problems with trailing slashes; it also | |
29 requires use of native Windows calls to allow atomic renames over | |
30 existing files. */ | |
11478
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
31 |
11972 | 32 # include <errno.h> |
12098 | 33 # include <stdbool.h> |
34 # include <stdlib.h> | |
35 # include <sys/stat.h> | |
36 # include <unistd.h> | |
11972 | 37 |
38 # define WIN32_LEAN_AND_MEAN | |
39 # include <windows.h> | |
11478
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
40 |
12098 | 41 # include "dirname.h" |
42 | |
11478
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
43 /* Rename the file SRC to DST. This replacement is necessary on |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
44 Windows, on which the system rename function will not replace |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
45 an existing DST. */ |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
46 int |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
47 rpl_rename (char const *src, char const *dst) |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
48 { |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
49 int error; |
12098 | 50 size_t src_len = strlen (src); |
51 size_t dst_len = strlen (dst); | |
52 char *src_base = last_component (src); | |
53 char *dst_base = last_component (dst); | |
54 bool src_slash; | |
55 bool dst_slash; | |
56 bool dst_exists; | |
57 struct stat src_st; | |
58 struct stat dst_st; | |
11478
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
59 |
12098 | 60 /* Filter out dot as last component. */ |
61 if (!src_len || !dst_len) | |
62 { | |
63 errno = ENOENT; | |
64 return -1; | |
65 } | |
66 if (*src_base == '.') | |
67 { | |
68 size_t len = base_len (src_base); | |
69 if (len == 1 || (len == 2 && src_base[1] == '.')) | |
70 { | |
71 errno = EINVAL; | |
72 return -1; | |
73 } | |
74 } | |
75 if (*dst_base == '.') | |
76 { | |
77 size_t len = base_len (dst_base); | |
78 if (len == 1 || (len == 2 && dst_base[1] == '.')) | |
79 { | |
80 errno = EINVAL; | |
81 return -1; | |
82 } | |
83 } | |
84 | |
85 /* Presence of a trailing slash requires directory semantics. If | |
86 the source does not exist, or if the destination cannot be turned | |
87 into a directory, give up now. Otherwise, strip trailing slashes | |
88 before calling rename. There are no symlinks on mingw, so stat | |
89 works instead of lstat. */ | |
90 src_slash = ISSLASH (src[src_len - 1]); | |
91 dst_slash = ISSLASH (dst[dst_len - 1]); | |
92 if (stat (src, &src_st)) | |
93 return -1; | |
94 if (stat (dst, &dst_st)) | |
95 { | |
96 if (errno != ENOENT || (!S_ISDIR (src_st.st_mode) && dst_slash)) | |
97 return -1; | |
98 dst_exists = false; | |
99 } | |
100 else | |
101 { | |
102 if (S_ISDIR (dst_st.st_mode) != S_ISDIR (src_st.st_mode)) | |
12102
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
103 { |
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
104 errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR; |
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
105 return -1; |
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
106 } |
12098 | 107 dst_exists = true; |
108 } | |
109 | |
110 /* There are no symlinks, so if a file existed with a trailing | |
111 slash, it must be a directory, and we don't have to worry about | |
112 stripping strip trailing slash. However, mingw refuses to | |
113 replace an existing empty directory, so we have to help it out. | |
114 And canonicalize_file_name is not yet ported to mingw; however, | |
115 for directories, getcwd works as a viable alternative. Ensure | |
12103
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
116 that we can get back to where we started before using it; later |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
117 attempts to return are fatal. Note that we can end up losing a |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
118 directory if rename then fails, but it was empty, so not much |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
119 damage was done. */ |
12098 | 120 if (dst_exists && S_ISDIR (dst_st.st_mode)) |
121 { | |
122 char *cwd = getcwd (NULL, 0); | |
123 char *src_temp; | |
124 char *dst_temp; | |
12103
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
125 if (!cwd || chdir (cwd)) |
12098 | 126 return -1; |
127 if (IS_ABSOLUTE_FILE_NAME (src)) | |
128 { | |
129 dst_temp = chdir (dst) ? NULL : getcwd (NULL, 0); | |
130 src_temp = chdir (src) ? NULL : getcwd (NULL, 0); | |
131 } | |
132 else | |
133 { | |
134 src_temp = chdir (src) ? NULL : getcwd (NULL, 0); | |
12103
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
135 if (!IS_ABSOLUTE_FILE_NAME (dst) && chdir (cwd)) |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
136 abort (); |
12098 | 137 dst_temp = chdir (dst) ? NULL : getcwd (NULL, 0); |
138 } | |
12103
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
139 if (chdir (cwd)) |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
140 abort (); |
12098 | 141 free (cwd); |
142 if (!src_temp || !dst_temp) | |
143 { | |
144 free (src_temp); | |
145 free (dst_temp); | |
146 errno = ENOMEM; | |
147 return -1; | |
148 } | |
149 src_len = strlen (src_temp); | |
150 if (strncmp (src_temp, dst_temp, src_len) == 0 | |
151 && (ISSLASH (dst_temp[src_len]) || dst_temp[src_len] == '\0')) | |
152 { | |
153 error = dst_temp[src_len]; | |
154 free (src_temp); | |
155 free (dst_temp); | |
156 if (error) | |
157 { | |
158 errno = EINVAL; | |
159 return -1; | |
160 } | |
161 return 0; | |
162 } | |
163 if (rmdir (dst)) | |
164 { | |
165 error = errno; | |
166 free (src_temp); | |
167 free (dst_temp); | |
168 errno = error; | |
169 return -1; | |
170 } | |
171 free (src_temp); | |
172 free (dst_temp); | |
173 } | |
174 | |
175 /* MoveFileEx works if SRC is a directory without any flags, but | |
176 fails with MOVEFILE_REPLACE_EXISTING, so try without flags first. | |
177 Thankfully, MoveFileEx handles hard links correctly, even though | |
178 rename() does not. */ | |
11478
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
179 if (MoveFileEx (src, dst, 0)) |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
180 return 0; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
181 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
182 /* Retry with MOVEFILE_REPLACE_EXISTING if the move failed |
12098 | 183 due to the destination already existing. */ |
11478
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
184 error = GetLastError (); |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
185 if (error == ERROR_FILE_EXISTS || error == ERROR_ALREADY_EXISTS) |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
186 { |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
187 if (MoveFileEx (src, dst, MOVEFILE_REPLACE_EXISTING)) |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
188 return 0; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
189 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
190 error = GetLastError (); |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
191 } |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
192 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
193 switch (error) |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
194 { |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
195 case ERROR_FILE_NOT_FOUND: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
196 case ERROR_PATH_NOT_FOUND: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
197 case ERROR_BAD_PATHNAME: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
198 case ERROR_DIRECTORY: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
199 errno = ENOENT; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
200 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
201 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
202 case ERROR_ACCESS_DENIED: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
203 case ERROR_SHARING_VIOLATION: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
204 errno = EACCES; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
205 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
206 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
207 case ERROR_OUTOFMEMORY: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
208 errno = ENOMEM; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
209 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
210 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
211 case ERROR_CURRENT_DIRECTORY: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
212 errno = EBUSY; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
213 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
214 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
215 case ERROR_NOT_SAME_DEVICE: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
216 errno = EXDEV; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
217 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
218 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
219 case ERROR_WRITE_PROTECT: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
220 errno = EROFS; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
221 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
222 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
223 case ERROR_WRITE_FAULT: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
224 case ERROR_READ_FAULT: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
225 case ERROR_GEN_FAILURE: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
226 errno = EIO; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
227 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
228 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
229 case ERROR_HANDLE_DISK_FULL: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
230 case ERROR_DISK_FULL: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
231 case ERROR_DISK_TOO_FRAGMENTED: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
232 errno = ENOSPC; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
233 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
234 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
235 case ERROR_FILE_EXISTS: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
236 case ERROR_ALREADY_EXISTS: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
237 errno = EEXIST; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
238 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
239 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
240 case ERROR_BUFFER_OVERFLOW: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
241 case ERROR_FILENAME_EXCED_RANGE: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
242 errno = ENAMETOOLONG; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
243 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
244 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
245 case ERROR_INVALID_NAME: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
246 case ERROR_DELETE_PENDING: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
247 errno = EPERM; /* ? */ |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
248 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
249 |
11972 | 250 # ifndef ERROR_FILE_TOO_LARGE |
12098 | 251 /* This value is documented but not defined in all versions of windows.h. */ |
11972 | 252 # define ERROR_FILE_TOO_LARGE 223 |
253 # endif | |
11478
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
254 case ERROR_FILE_TOO_LARGE: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
255 errno = EFBIG; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
256 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
257 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
258 default: |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
259 errno = EINVAL; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
260 break; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
261 } |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
262 |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
263 return -1; |
df9a59a3f8bc
Make rename replace existing destinations on Windows.
Ben Pfaff <blp@cs.stanford.edu>
parents:
9309
diff
changeset
|
264 } |
11972 | 265 |
266 #else /* ! W32 platform */ | |
3102
a17f2d7d7ee1
Include stdlib.h, string.h or strings.h, and xalloc.h.
Jim Meyering <jim@meyering.net>
parents:
3099
diff
changeset
|
267 |
12095 | 268 # include <errno.h> |
269 # include <stdio.h> | |
270 # include <stdlib.h> | |
271 # include <string.h> | |
272 # include <sys/stat.h> | |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
273 # include <unistd.h> |
12093 | 274 |
12095 | 275 # include "dirname.h" |
276 # include "same-inode.h" | |
3099 | 277 |
12093 | 278 /* Rename the file SRC to DST, fixing any trailing slash bugs. */ |
3099 | 279 |
280 int | |
5907 | 281 rpl_rename (char const *src, char const *dst) |
3099 | 282 { |
12093 | 283 size_t src_len = strlen (src); |
284 size_t dst_len = strlen (dst); | |
285 char *src_temp = (char *) src; | |
286 char *dst_temp = (char *) dst; | |
287 bool src_slash; | |
288 bool dst_slash; | |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
289 bool dst_exists; |
12093 | 290 int ret_val = -1; |
291 int rename_errno = ENOTDIR; | |
292 struct stat src_st; | |
293 struct stat dst_st; | |
294 | |
295 if (!src_len || !dst_len) | |
296 return rename (src, dst); /* Let strace see the ENOENT failure. */ | |
3099 | 297 |
12095 | 298 # if RENAME_DEST_EXISTS_BUG |
299 { | |
300 char *src_base = last_component (src); | |
301 char *dst_base = last_component (dst); | |
302 if (*src_base == '.') | |
303 { | |
304 size_t len = base_len (src_base); | |
305 if (len == 1 || (len == 2 && src_base[1] == '.')) | |
306 { | |
307 errno = EINVAL; | |
308 return -1; | |
309 } | |
310 } | |
311 if (*dst_base == '.') | |
312 { | |
313 size_t len = base_len (dst_base); | |
314 if (len == 1 || (len == 2 && dst_base[1] == '.')) | |
315 { | |
316 errno = EINVAL; | |
317 return -1; | |
318 } | |
319 } | |
320 } | |
321 # endif /* RENAME_DEST_EXISTS_BUG */ | |
322 | |
12093 | 323 src_slash = src[src_len - 1] == '/'; |
324 dst_slash = dst[dst_len - 1] == '/'; | |
12095 | 325 |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
326 # if !RENAME_HARD_LINK_BUG && !RENAME_DEST_EXISTS_BUG |
12095 | 327 /* If there are no trailing slashes, then trust the native |
328 implementation unless we also suspect issues with hard link | |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
329 detection or file/directory conflicts. */ |
12093 | 330 if (!src_slash && !dst_slash) |
331 return rename (src, dst); | |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
332 # endif /* !RENAME_HARD_LINK_BUG && !RENAME_DEST_EXISTS_BUG */ |
12093 | 333 |
334 /* Presence of a trailing slash requires directory semantics. If | |
335 the source does not exist, or if the destination cannot be turned | |
336 into a directory, give up now. Otherwise, strip trailing slashes | |
337 before calling rename. */ | |
338 if (lstat (src, &src_st)) | |
339 return -1; | |
340 if (lstat (dst, &dst_st)) | |
341 { | |
12095 | 342 if (errno != ENOENT || (!S_ISDIR (src_st.st_mode) && dst_slash)) |
12093 | 343 return -1; |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
344 dst_exists = false; |
12093 | 345 } |
12095 | 346 else |
12093 | 347 { |
12095 | 348 if (S_ISDIR (dst_st.st_mode) != S_ISDIR (src_st.st_mode)) |
12102
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
349 { |
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
350 errno = S_ISDIR (dst_st.st_mode) ? EISDIR : ENOTDIR; |
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
351 return -1; |
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
352 } |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
353 # if RENAME_HARD_LINK_BUG |
12095 | 354 if (SAME_INODE (src_st, dst_st)) |
12102
3ba227551e1d
maint: cleanup whitespace in recent commits
Eric Blake <ebb9@byu.net>
parents:
12098
diff
changeset
|
355 return 0; |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
356 # endif /* RENAME_HARD_LINK_BUG */ |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
357 dst_exists = true; |
3099 | 358 } |
359 | |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
360 # if (RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG \ |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
361 || RENAME_HARD_LINK_BUG) |
12094 | 362 /* If the only bug was that a trailing slash was allowed on a |
363 non-existing file destination, as in Solaris 10, then we've | |
364 already covered that situation. But if there is any problem with | |
365 a trailing slash on an existing source or destination, as in | |
12095 | 366 Solaris 9, or if a directory can overwrite a symlink, as on |
12096
1f43035b0900
rename-dest-slash: merge into rename module
Eric Blake <ebb9@byu.net>
parents:
12095
diff
changeset
|
367 Cygwin 1.5, or if directories cannot be created with trailing |
1f43035b0900
rename-dest-slash: merge into rename module
Eric Blake <ebb9@byu.net>
parents:
12095
diff
changeset
|
368 slash, as on NetBSD 1.6, then we must strip the offending slash |
1f43035b0900
rename-dest-slash: merge into rename module
Eric Blake <ebb9@byu.net>
parents:
12095
diff
changeset
|
369 and check that we have not encountered a symlink instead of a |
1f43035b0900
rename-dest-slash: merge into rename module
Eric Blake <ebb9@byu.net>
parents:
12095
diff
changeset
|
370 directory. |
12094 | 371 |
372 Stripping a trailing slash interferes with POSIX semantics, where | |
373 rename behavior on a symlink with a trailing slash operates on | |
374 the corresponding target directory. We prefer the GNU semantics | |
375 of rejecting any use of a symlink with trailing slash, but do not | |
376 enforce them, since Solaris 10 is able to obey POSIX semantics | |
377 and there might be clients expecting it, as counter-intuitive as | |
378 those semantics are. | |
379 | |
380 Technically, we could also follow the POSIX behavior by chasing a | |
381 readlink trail, but that is harder to implement. */ | |
12093 | 382 if (src_slash) |
383 { | |
384 src_temp = strdup (src); | |
385 if (!src_temp) | |
386 { | |
387 /* Rather than rely on strdup-posix, we set errno ourselves. */ | |
388 rename_errno = ENOMEM; | |
389 goto out; | |
390 } | |
391 strip_trailing_slashes (src_temp); | |
392 if (lstat (src_temp, &src_st)) | |
393 { | |
394 rename_errno = errno; | |
395 goto out; | |
396 } | |
397 if (S_ISLNK (src_st.st_mode)) | |
398 goto out; | |
399 } | |
400 if (dst_slash) | |
401 { | |
402 dst_temp = strdup (dst); | |
403 if (!dst_temp) | |
404 { | |
405 rename_errno = ENOMEM; | |
406 goto out; | |
407 } | |
408 strip_trailing_slashes (dst_temp); | |
409 if (lstat (dst_temp, &dst_st)) | |
410 { | |
411 if (errno != ENOENT) | |
412 { | |
413 rename_errno = errno; | |
414 goto out; | |
415 } | |
416 } | |
417 else if (S_ISLNK (dst_st.st_mode)) | |
418 goto out; | |
419 } | |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
420 # endif /* RENAME_TRAILING_SLASH_SOURCE_BUG || RENAME_DEST_EXISTS_BUG |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
421 || RENAME_HARD_LINK_BUG */ |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
422 |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
423 # if RENAME_DEST_EXISTS_BUG |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
424 /* Cygwin 1.5 sometimes behaves oddly when moving a non-empty |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
425 directory on top of an empty one (the old directory name can |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
426 reappear if the new directory tree is removed). Work around this |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
427 by removing the target first, but don't remove the target if it |
12103
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
428 is a subdirectory of the source. Note that we can end up losing |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
429 a directory if rename then fails, but it was empty, so not much |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
430 damage was done. */ |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
431 if (dst_exists && S_ISDIR (dst_st.st_mode)) |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
432 { |
12103
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
433 if (src_st.st_dev != dst_st.st_dev) |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
434 { |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
435 rename_errno = EXDEV; |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
436 goto out; |
832357ca223a
rename, fchdir: don't ignore chdir failure
Eric Blake <ebb9@byu.net>
parents:
12102
diff
changeset
|
437 } |
12097
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
438 if (src_temp != src) |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
439 free (src_temp); |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
440 src_temp = canonicalize_file_name (src); |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
441 if (dst_temp != dst) |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
442 free (dst_temp); |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
443 dst_temp = canonicalize_file_name (dst); |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
444 if (!src_temp || !dst_temp) |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
445 { |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
446 rename_errno = ENOMEM; |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
447 goto out; |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
448 } |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
449 src_len = strlen (src_temp); |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
450 if (strncmp (src_temp, dst_temp, src_len) == 0 |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
451 && dst_temp[src_len] == '/') |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
452 { |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
453 rename_errno = EINVAL; |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
454 goto out; |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
455 } |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
456 if (rmdir (dst)) |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
457 { |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
458 rename_errno = errno; |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
459 goto out; |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
460 } |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
461 } |
bb32464985d7
rename: fix another cygwin 1.5 bug
Eric Blake <ebb9@byu.net>
parents:
12096
diff
changeset
|
462 # endif /* RENAME_DEST_EXISTS_BUG */ |
12094 | 463 |
12093 | 464 ret_val = rename (src_temp, dst_temp); |
465 rename_errno = errno; | |
466 out: | |
5907 | 467 if (src_temp != src) |
3099 | 468 free (src_temp); |
12093 | 469 if (dst_temp != dst) |
470 free (dst_temp); | |
471 errno = rename_errno; | |
3102
a17f2d7d7ee1
Include stdlib.h, string.h or strings.h, and xalloc.h.
Jim Meyering <jim@meyering.net>
parents:
3099
diff
changeset
|
472 return ret_val; |
3099 | 473 } |
11972 | 474 #endif /* ! W32 platform */ |