/src/server/mysys/my_symlink.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | Copyright (c) 2001, 2011, Oracle and/or its affiliates |
3 | | Copyright (c) 2010, 2022, 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-1335 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 | 0 | { |
29 | 0 | return 0; |
30 | 0 | } |
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 | 0 | { |
47 | | #ifndef HAVE_READLINK |
48 | | strnmov(to, filename, FN_REFLEN); |
49 | | return 1; |
50 | | #else |
51 | 0 | int result=0; |
52 | 0 | int length; |
53 | 0 | DBUG_ENTER("my_readlink"); |
54 | |
|
55 | 0 | if ((length=readlink(filename, to, FN_REFLEN-1)) < 0) |
56 | 0 | { |
57 | | /* Don't give an error if this wasn't a symlink */ |
58 | 0 | if ((my_errno=errno) == EINVAL) |
59 | 0 | { |
60 | 0 | result= 1; |
61 | 0 | strnmov(to, filename, FN_REFLEN); |
62 | 0 | } |
63 | 0 | else |
64 | 0 | { |
65 | 0 | if (MyFlags & MY_WME) |
66 | 0 | my_error(EE_CANT_READLINK, MYF(0), filename, errno); |
67 | 0 | result= -1; |
68 | 0 | } |
69 | 0 | } |
70 | 0 | else |
71 | 0 | to[length]=0; |
72 | 0 | DBUG_PRINT("exit" ,("result: %d", result)); |
73 | 0 | DBUG_RETURN(result); |
74 | 0 | #endif /* HAVE_READLINK */ |
75 | 0 | } |
76 | | |
77 | | |
78 | | /* Create a symbolic link */ |
79 | | |
80 | | int my_symlink(const char *content, const char *linkname, myf MyFlags) |
81 | 0 | { |
82 | | #ifndef HAVE_READLINK |
83 | | return 0; |
84 | | #else |
85 | 0 | int result; |
86 | 0 | DBUG_ENTER("my_symlink"); |
87 | 0 | DBUG_PRINT("enter",("content: %s linkname: %s", content, linkname)); |
88 | |
|
89 | 0 | result= 0; |
90 | 0 | if (symlink(content, linkname)) |
91 | 0 | { |
92 | 0 | result= -1; |
93 | 0 | my_errno=errno; |
94 | 0 | if (MyFlags & MY_WME) |
95 | 0 | my_error(EE_CANT_SYMLINK, MYF(0), linkname, content, errno); |
96 | 0 | } |
97 | 0 | else if ((MyFlags & MY_SYNC_DIR) && my_sync_dir_by_file(linkname, MyFlags)) |
98 | 0 | result= -1; |
99 | 0 | DBUG_RETURN(result); |
100 | 0 | #endif /* HAVE_READLINK */ |
101 | 0 | } |
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 | 0 | { |
114 | 0 | #if defined (HAVE_LSTAT) && defined (S_ISLNK) |
115 | 0 | struct stat stat_buff; |
116 | 0 | if (lstat(filename, &stat_buff)) |
117 | 0 | return 0; |
118 | 0 | MSAN_STAT_WORKAROUND(&stat_buff); |
119 | 0 | return !!S_ISLNK(stat_buff.st_mode); |
120 | | #elif defined (_WIN32) |
121 | | DWORD dwAttr = GetFileAttributes(filename); |
122 | | return (dwAttr != INVALID_FILE_ATTRIBUTES) && |
123 | | (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT); |
124 | | #else /* No symlinks */ |
125 | | return 0; |
126 | | #endif |
127 | 0 | } |
128 | | |
129 | | /* |
130 | | Resolve all symbolic links in path |
131 | | 'to' may be equal to 'filename' |
132 | | |
133 | | to is guaranteed to never set to a string longer than FN_REFLEN |
134 | | (including the end \0) |
135 | | |
136 | | On error returns -1, unless error is file not found, in which case it |
137 | | is 1. |
138 | | |
139 | | Sets my_errno to specific error number. |
140 | | */ |
141 | | |
142 | | int my_realpath(char *to, const char *filename, myf MyFlags) |
143 | 0 | { |
144 | 0 | #if defined(HAVE_REALPATH) && !defined(HAVE_BROKEN_REALPATH) |
145 | 0 | int result=0; |
146 | 0 | char buff[BUFF_LEN]; |
147 | 0 | char *ptr; |
148 | 0 | DBUG_ENTER("my_realpath"); |
149 | |
|
150 | 0 | DBUG_PRINT("info",("executing realpath")); |
151 | 0 | if ((ptr=realpath(filename,buff))) |
152 | 0 | strmake(to, ptr, FN_REFLEN-1); |
153 | 0 | else |
154 | 0 | { |
155 | | /* |
156 | | Realpath didn't work; Use my_load_path() which is a poor substitute |
157 | | original name but will at least be able to resolve paths that starts |
158 | | with '.'. |
159 | | */ |
160 | 0 | if (MyFlags) |
161 | 0 | DBUG_PRINT("error",("realpath failed with errno: %d", errno)); |
162 | 0 | my_errno=errno; |
163 | 0 | if (MyFlags & MY_WME) |
164 | 0 | my_error(EE_REALPATH, MYF(0), filename, my_errno); |
165 | 0 | my_load_path(to, filename, NullS); |
166 | 0 | if (my_errno == ENOENT) |
167 | 0 | result= 1; |
168 | 0 | else |
169 | 0 | result= -1; |
170 | 0 | } |
171 | 0 | DBUG_RETURN(result); |
172 | | #elif defined(_WIN32) |
173 | | int ret= GetFullPathName(filename,FN_REFLEN, to, NULL); |
174 | | if (ret == 0 || ret > FN_REFLEN) |
175 | | { |
176 | | my_errno= (ret > FN_REFLEN) ? ENAMETOOLONG : GetLastError(); |
177 | | if (MyFlags & MY_WME) |
178 | | my_error(EE_REALPATH, MYF(0), filename, my_errno); |
179 | | /* |
180 | | GetFullPathName didn't work : use my_load_path() which is a poor |
181 | | substitute original name but will at least be able to resolve |
182 | | paths that starts with '.'. |
183 | | */ |
184 | | my_load_path(to, filename, NullS); |
185 | | return -1; |
186 | | } |
187 | | #else |
188 | | my_load_path(to, filename, NullS); |
189 | | #endif |
190 | 0 | return 0; |
191 | 0 | } |
192 | | |
193 | | #ifdef HAVE_OPEN_PARENT_DIR_NOSYMLINKS |
194 | | /** opens the parent dir. walks the path, and does not resolve symlinks |
195 | | |
196 | | returns the pointer to the file name (basename) within the pathname |
197 | | or NULL in case of an error |
198 | | |
199 | | stores the parent dir (dirname) file descriptor in pdfd. |
200 | | It can be -1 even if there was no error! |
201 | | |
202 | | This is used for symlinked tables for DATA/INDEX DIRECTORY. |
203 | | The paths there have been realpath()-ed. So, we can assume here that |
204 | | |
205 | | * `pathname` is an absolute path |
206 | | * no '.', '..', and '//' in the path |
207 | | * file exists |
208 | | */ |
209 | | |
210 | | const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd) |
211 | 0 | { |
212 | 0 | char buf[FN_REFLEN + 1]; |
213 | 0 | char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf)); |
214 | 0 | int fd, dfd= -1; |
215 | |
|
216 | 0 | if (*end) |
217 | 0 | { |
218 | 0 | errno= ENAMETOOLONG; |
219 | 0 | return NULL; |
220 | 0 | } |
221 | | |
222 | 0 | if (*s != '/') /* not an absolute path */ |
223 | 0 | { |
224 | 0 | errno= ENOENT; |
225 | 0 | return NULL; |
226 | 0 | } |
227 | | |
228 | 0 | for (;;) |
229 | 0 | { |
230 | 0 | if (*e == '/') /* '//' in the path */ |
231 | 0 | { |
232 | 0 | errno= ENOENT; |
233 | 0 | goto err; |
234 | 0 | } |
235 | 0 | while (*e && *e != '/') |
236 | 0 | e++; |
237 | 0 | *e= 0; |
238 | |
|
239 | 0 | if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3)) |
240 | 0 | { |
241 | 0 | errno= ENOENT; |
242 | 0 | goto err; |
243 | 0 | } |
244 | | |
245 | 0 | if (++e >= end) |
246 | 0 | { |
247 | 0 | *pdfd= dfd; |
248 | 0 | return pathname + (s - buf); |
249 | 0 | } |
250 | | |
251 | 0 | fd = openat(dfd, s, O_NOFOLLOW | O_PATH | O_CLOEXEC); |
252 | 0 | if (fd < 0) |
253 | 0 | goto err; |
254 | | |
255 | 0 | if (dfd >= 0) |
256 | 0 | close(dfd); |
257 | |
|
258 | 0 | dfd= fd; |
259 | 0 | s= e; |
260 | 0 | } |
261 | 0 | err: |
262 | 0 | if (dfd >= 0) |
263 | 0 | close(dfd); |
264 | 0 | return NULL; |
265 | 0 | } |
266 | | #endif |