12099
|
1 /* Rename a file relative to open directories. |
|
2 Copyright (C) 2009 Free Software Foundation, Inc. |
|
3 |
|
4 This program is free software: you can redistribute it and/or modify |
|
5 it under the terms of the GNU General Public License as published by |
|
6 the Free Software Foundation; either version 3 of the License, or |
|
7 (at your option) any later version. |
|
8 |
|
9 This program is distributed in the hope that it will be useful, |
|
10 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 GNU General Public License for more details. |
|
13 |
|
14 You should have received a copy of the GNU General Public License |
|
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */ |
|
16 |
|
17 /* written by Eric Blake */ |
|
18 |
|
19 #include <config.h> |
|
20 |
|
21 #include <stdio.h> |
|
22 |
12100
|
23 #if HAVE_RENAMEAT |
|
24 |
|
25 # include <errno.h> |
|
26 # include <stdbool.h> |
|
27 # include <stdlib.h> |
|
28 # include <string.h> |
|
29 # include <sys/stat.h> |
|
30 |
|
31 # include "dirname.h" |
|
32 # include "openat.h" |
|
33 |
|
34 # undef renameat |
|
35 |
|
36 /* renameat does not honor trailing / on Solaris 10. Solve it in a |
|
37 similar manner to rename. No need to worry about bugs not present |
|
38 on Solaris, since all other systems either lack renameat or honor |
|
39 trailing slash correctly. */ |
|
40 |
|
41 int |
|
42 rpl_renameat (int fd1, char const *src, int fd2, char const *dst) |
|
43 { |
|
44 size_t src_len = strlen (src); |
|
45 size_t dst_len = strlen (dst); |
|
46 char *src_temp = (char *) src; |
|
47 char *dst_temp = (char *) dst; |
|
48 bool src_slash; |
|
49 bool dst_slash; |
|
50 int ret_val = -1; |
|
51 int rename_errno = ENOTDIR; |
|
52 struct stat src_st; |
|
53 struct stat dst_st; |
|
54 |
|
55 /* Let strace see any ENOENT failure. */ |
|
56 if (!src_len || !dst_len) |
|
57 return renameat (fd1, src, fd2, dst); |
|
58 |
|
59 src_slash = src[src_len - 1] == '/'; |
|
60 dst_slash = dst[dst_len - 1] == '/'; |
|
61 if (!src_slash && !dst_slash) |
|
62 return renameat (fd1, src, fd2, dst); |
|
63 |
|
64 /* Presence of a trailing slash requires directory semantics. If |
|
65 the source does not exist, or if the destination cannot be turned |
|
66 into a directory, give up now. Otherwise, strip trailing slashes |
|
67 before calling rename. */ |
|
68 if (lstatat (fd1, src, &src_st)) |
|
69 return -1; |
|
70 if (lstatat (fd2, dst, &dst_st)) |
|
71 { |
|
72 if (errno != ENOENT || !S_ISDIR (src_st.st_mode)) |
|
73 return -1; |
|
74 } |
|
75 else if (!S_ISDIR (dst_st.st_mode)) |
|
76 { |
|
77 errno = ENOTDIR; |
|
78 return -1; |
|
79 } |
|
80 else if (!S_ISDIR (src_st.st_mode)) |
|
81 { |
|
82 errno = EISDIR; |
|
83 return -1; |
|
84 } |
|
85 |
|
86 # if RENAME_TRAILING_SLASH_SOURCE_BUG |
|
87 /* See the lengthy comment in rename.c why Solaris 9 is forced to |
|
88 GNU behavior, while Solaris 10 is left with POSIX behavior, |
|
89 regarding symlinks with trailing slash. */ |
|
90 if (src_slash) |
|
91 { |
|
92 src_temp = strdup (src); |
|
93 if (!src_temp) |
|
94 { |
|
95 /* Rather than rely on strdup-posix, we set errno ourselves. */ |
|
96 rename_errno = ENOMEM; |
|
97 goto out; |
|
98 } |
|
99 strip_trailing_slashes (src_temp); |
|
100 if (lstatat (fd1, src_temp, &src_st)) |
|
101 { |
|
102 rename_errno = errno; |
|
103 goto out; |
|
104 } |
|
105 if (S_ISLNK (src_st.st_mode)) |
|
106 goto out; |
|
107 } |
|
108 if (dst_slash) |
|
109 { |
|
110 dst_temp = strdup (dst); |
|
111 if (!dst_temp) |
|
112 { |
|
113 rename_errno = ENOMEM; |
|
114 goto out; |
|
115 } |
|
116 strip_trailing_slashes (dst_temp); |
|
117 if (lstatat (fd2, dst_temp, &dst_st)) |
|
118 { |
|
119 if (errno != ENOENT) |
|
120 { |
|
121 rename_errno = errno; |
|
122 goto out; |
|
123 } |
|
124 } |
|
125 else if (S_ISLNK (dst_st.st_mode)) |
|
126 goto out; |
|
127 } |
|
128 # endif /* RENAME_TRAILING_SLASH_SOURCE_BUG */ |
|
129 |
|
130 ret_val = renameat (fd1, src_temp, fd2, dst_temp); |
|
131 rename_errno = errno; |
|
132 out: |
|
133 if (src_temp != src) |
|
134 free (src_temp); |
|
135 if (dst_temp != dst) |
|
136 free (dst_temp); |
|
137 errno = rename_errno; |
|
138 return ret_val; |
|
139 } |
|
140 |
|
141 #else /* !HAVE_RENAMEAT */ |
|
142 |
|
143 # include "openat-priv.h" |
12099
|
144 |
|
145 /* Rename FILE1, in the directory open on descriptor FD1, to FILE2, in |
|
146 the directory open on descriptor FD2. If possible, do it without |
|
147 changing the working directory. Otherwise, resort to using |
|
148 save_cwd/fchdir, then rename/restore_cwd. If either the save_cwd or |
|
149 the restore_cwd fails, then give a diagnostic and exit nonzero. */ |
|
150 |
|
151 int |
|
152 renameat (int fd1, char const *file1, int fd2, char const *file2) |
|
153 { |
|
154 return at_func2 (fd1, file1, fd2, file2, rename); |
|
155 } |
12100
|
156 |
|
157 #endif /* !HAVE_RENAMEAT */ |