1 | /* |
2 | Copyright (c) 2001, 2011, Oracle and/or its affiliates |
3 | Copyright (c) 2010, 2017, MariaDB |
4 | |
5 | This program is free software; you can redistribute it and/or modify |
6 | it under the terms of the GNU General Public License as published by |
7 | the Free Software Foundation; version 2 of the License. |
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, write to the Free Software |
16 | Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ |
17 | |
18 | #include "mysys_priv.h" |
19 | #include "mysys_err.h" |
20 | #include <m_string.h> |
21 | #include <errno.h> |
22 | #ifdef HAVE_REALPATH |
23 | #include <sys/param.h> |
24 | #include <sys/stat.h> |
25 | #endif |
26 | |
27 | static int always_valid(const char *filename __attribute__((unused))) |
28 | { |
29 | return 0; |
30 | } |
31 | |
32 | int (*mysys_test_invalid_symlink)(const char *filename)= always_valid; |
33 | |
34 | |
35 | /* |
36 | Reads the content of a symbolic link |
37 | If the file is not a symbolic link, return the original file name in to. |
38 | |
39 | RETURN |
40 | 0 If filename was a symlink, (to will be set to value of symlink) |
41 | 1 If filename was a normal file (to will be set to filename) |
42 | -1 on error. |
43 | */ |
44 | |
45 | int my_readlink(char *to, const char *filename, myf MyFlags) |
46 | { |
47 | #ifndef HAVE_READLINK |
48 | strmov(to,filename); |
49 | return 1; |
50 | #else |
51 | int result=0; |
52 | int length; |
53 | DBUG_ENTER("my_readlink" ); |
54 | |
55 | if ((length=readlink(filename, to, FN_REFLEN-1)) < 0) |
56 | { |
57 | /* Don't give an error if this wasn't a symlink */ |
58 | if ((my_errno=errno) == EINVAL) |
59 | { |
60 | result= 1; |
61 | strmov(to,filename); |
62 | } |
63 | else |
64 | { |
65 | if (MyFlags & MY_WME) |
66 | my_error(EE_CANT_READLINK, MYF(0), filename, errno); |
67 | result= -1; |
68 | } |
69 | } |
70 | else |
71 | to[length]=0; |
72 | DBUG_PRINT("exit" ,("result: %d" , result)); |
73 | DBUG_RETURN(result); |
74 | #endif /* HAVE_READLINK */ |
75 | } |
76 | |
77 | |
78 | /* Create a symbolic link */ |
79 | |
80 | int my_symlink(const char *content, const char *linkname, myf MyFlags) |
81 | { |
82 | #ifndef HAVE_READLINK |
83 | return 0; |
84 | #else |
85 | int result; |
86 | DBUG_ENTER("my_symlink" ); |
87 | DBUG_PRINT("enter" ,("content: %s linkname: %s" , content, linkname)); |
88 | |
89 | result= 0; |
90 | if (symlink(content, linkname)) |
91 | { |
92 | result= -1; |
93 | my_errno=errno; |
94 | if (MyFlags & MY_WME) |
95 | my_error(EE_CANT_SYMLINK, MYF(0), linkname, content, errno); |
96 | } |
97 | else if ((MyFlags & MY_SYNC_DIR) && my_sync_dir_by_file(linkname, MyFlags)) |
98 | result= -1; |
99 | DBUG_RETURN(result); |
100 | #endif /* HAVE_READLINK */ |
101 | } |
102 | |
103 | #if defined(SCO) |
104 | #define BUFF_LEN 4097 |
105 | #elif defined(MAXPATHLEN) |
106 | #define BUFF_LEN MAXPATHLEN |
107 | #else |
108 | #define BUFF_LEN FN_LEN |
109 | #endif |
110 | |
111 | |
112 | int my_is_symlink(const char *filename __attribute__((unused))) |
113 | { |
114 | #if defined (HAVE_LSTAT) && defined (S_ISLNK) |
115 | struct stat stat_buff; |
116 | return !lstat(filename, &stat_buff) && S_ISLNK(stat_buff.st_mode); |
117 | #elif defined (_WIN32) |
118 | DWORD dwAttr = GetFileAttributes(filename); |
119 | return (dwAttr != INVALID_FILE_ATTRIBUTES) && |
120 | (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT); |
121 | #else /* No symlinks */ |
122 | return 0; |
123 | #endif |
124 | } |
125 | |
126 | /* |
127 | Resolve all symbolic links in path |
128 | 'to' may be equal to 'filename' |
129 | |
130 | to is guaranteed to never set to a string longer than FN_REFLEN |
131 | (including the end \0) |
132 | |
133 | On error returns -1, unless error is file not found, in which case it |
134 | is 1. |
135 | |
136 | Sets my_errno to specific error number. |
137 | */ |
138 | |
139 | int my_realpath(char *to, const char *filename, myf MyFlags) |
140 | { |
141 | #if defined(HAVE_REALPATH) && !defined(HAVE_BROKEN_REALPATH) |
142 | int result=0; |
143 | char buff[BUFF_LEN]; |
144 | char *ptr; |
145 | DBUG_ENTER("my_realpath" ); |
146 | |
147 | DBUG_PRINT("info" ,("executing realpath" )); |
148 | if ((ptr=realpath(filename,buff))) |
149 | strmake(to, ptr, FN_REFLEN-1); |
150 | else |
151 | { |
152 | /* |
153 | Realpath didn't work; Use my_load_path() which is a poor substitute |
154 | original name but will at least be able to resolve paths that starts |
155 | with '.'. |
156 | */ |
157 | DBUG_PRINT("error" ,("realpath failed with errno: %d" , errno)); |
158 | my_errno=errno; |
159 | if (MyFlags & MY_WME) |
160 | my_error(EE_REALPATH, MYF(0), filename, my_errno); |
161 | my_load_path(to, filename, NullS); |
162 | if (my_errno == ENOENT) |
163 | result= 1; |
164 | else |
165 | result= -1; |
166 | } |
167 | DBUG_RETURN(result); |
168 | #elif defined(_WIN32) |
169 | int ret= GetFullPathName(filename,FN_REFLEN, to, NULL); |
170 | if (ret == 0 || ret > FN_REFLEN) |
171 | { |
172 | my_errno= (ret > FN_REFLEN) ? ENAMETOOLONG : GetLastError(); |
173 | if (MyFlags & MY_WME) |
174 | my_error(EE_REALPATH, MYF(0), filename, my_errno); |
175 | /* |
176 | GetFullPathName didn't work : use my_load_path() which is a poor |
177 | substitute original name but will at least be able to resolve |
178 | paths that starts with '.'. |
179 | */ |
180 | my_load_path(to, filename, NullS); |
181 | return -1; |
182 | } |
183 | #else |
184 | my_load_path(to, filename, NullS); |
185 | #endif |
186 | return 0; |
187 | } |
188 | |
189 | #ifdef HAVE_OPEN_PARENT_DIR_NOSYMLINKS |
190 | /** opens the parent dir. walks the path, and does not resolve symlinks |
191 | |
192 | returns the pointer to the file name (basename) within the pathname |
193 | or NULL in case of an error |
194 | |
195 | stores the parent dir (dirname) file descriptor in pdfd. |
196 | It can be -1 even if there was no error! |
197 | |
198 | This is used for symlinked tables for DATA/INDEX DIRECTORY. |
199 | The paths there have been realpath()-ed. So, we can assume here that |
200 | |
201 | * `pathname` is an absolute path |
202 | * no '.', '..', and '//' in the path |
203 | * file exists |
204 | */ |
205 | |
206 | const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd) |
207 | { |
208 | char buf[FN_REFLEN + 1]; |
209 | char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf)); |
210 | int fd, dfd= -1; |
211 | |
212 | if (*end) |
213 | { |
214 | errno= ENAMETOOLONG; |
215 | return NULL; |
216 | } |
217 | |
218 | if (*s != '/') /* not an absolute path */ |
219 | { |
220 | errno= ENOENT; |
221 | return NULL; |
222 | } |
223 | |
224 | for (;;) |
225 | { |
226 | if (*e == '/') /* '//' in the path */ |
227 | { |
228 | errno= ENOENT; |
229 | goto err; |
230 | } |
231 | while (*e && *e != '/') |
232 | e++; |
233 | *e= 0; |
234 | |
235 | if (!memcmp(s, "." , 2) || !memcmp(s, ".." , 3)) |
236 | { |
237 | errno= ENOENT; |
238 | goto err; |
239 | } |
240 | |
241 | if (++e >= end) |
242 | { |
243 | *pdfd= dfd; |
244 | return pathname + (s - buf); |
245 | } |
246 | |
247 | fd = openat(dfd, s, O_NOFOLLOW | O_PATH); |
248 | if (fd < 0) |
249 | goto err; |
250 | |
251 | if (dfd >= 0) |
252 | close(dfd); |
253 | |
254 | dfd= fd; |
255 | s= e; |
256 | } |
257 | err: |
258 | if (dfd >= 0) |
259 | close(dfd); |
260 | return NULL; |
261 | } |
262 | #endif |
263 | |