/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 | return !!S_ISLNK(stat_buff.st_mode); |
119 | | #elif defined (_WIN32) |
120 | | DWORD dwAttr = GetFileAttributes(filename); |
121 | | return (dwAttr != INVALID_FILE_ATTRIBUTES) && |
122 | | (dwAttr & FILE_ATTRIBUTE_REPARSE_POINT); |
123 | | #else /* No symlinks */ |
124 | | return 0; |
125 | | #endif |
126 | 0 | } |
127 | | |
128 | | /* |
129 | | Resolve all symbolic links in path |
130 | | 'to' may be equal to 'filename' |
131 | | |
132 | | to is guaranteed to never set to a string longer than FN_REFLEN |
133 | | (including the end \0) |
134 | | |
135 | | On error returns -1, unless error is file not found, in which case it |
136 | | is 1. |
137 | | |
138 | | Sets my_errno to specific error number. |
139 | | */ |
140 | | |
141 | | int my_realpath(char *to, const char *filename, myf MyFlags) |
142 | 0 | { |
143 | 0 | #if defined(HAVE_REALPATH) |
144 | 0 | int result=0; |
145 | 0 | char buff[BUFF_LEN]; |
146 | 0 | char *ptr; |
147 | 0 | DBUG_ENTER("my_realpath"); |
148 | |
|
149 | 0 | DBUG_PRINT("info",("executing realpath")); |
150 | 0 | if ((ptr=realpath(filename,buff))) |
151 | 0 | strmake(to, ptr, FN_REFLEN-1); |
152 | 0 | else |
153 | 0 | { |
154 | | /* |
155 | | Realpath didn't work; Use my_load_path() which is a poor substitute |
156 | | original name but will at least be able to resolve paths that starts |
157 | | with '.'. |
158 | | */ |
159 | 0 | if (MyFlags) |
160 | 0 | DBUG_PRINT("error",("realpath failed with errno: %d", errno)); |
161 | 0 | my_errno=errno; |
162 | 0 | if (MyFlags & MY_WME) |
163 | 0 | my_error(EE_REALPATH, MYF(0), filename, my_errno); |
164 | 0 | my_load_path(to, filename, NullS); |
165 | 0 | if (my_errno == ENOENT) |
166 | 0 | result= 1; |
167 | 0 | else |
168 | 0 | result= -1; |
169 | 0 | } |
170 | 0 | DBUG_RETURN(result); |
171 | | #elif defined(_WIN32) |
172 | | int ret= GetFullPathName(filename,FN_REFLEN, to, NULL); |
173 | | if (ret == 0 || ret > FN_REFLEN) |
174 | | { |
175 | | my_errno= (ret > FN_REFLEN) ? ENAMETOOLONG : GetLastError(); |
176 | | if (MyFlags & MY_WME) |
177 | | my_error(EE_REALPATH, MYF(0), filename, my_errno); |
178 | | /* |
179 | | GetFullPathName didn't work : use my_load_path() which is a poor |
180 | | substitute original name but will at least be able to resolve |
181 | | paths that starts with '.'. |
182 | | */ |
183 | | my_load_path(to, filename, NullS); |
184 | | return -1; |
185 | | } |
186 | | #else |
187 | | my_load_path(to, filename, NullS); |
188 | | #endif |
189 | 0 | return 0; |
190 | 0 | } |
191 | | |
192 | | #ifdef HAVE_OPEN_PARENT_DIR_NOSYMLINKS |
193 | | /** opens the parent dir. walks the path, and does not resolve symlinks |
194 | | |
195 | | returns the pointer to the file name (basename) within the pathname |
196 | | or NULL in case of an error |
197 | | |
198 | | stores the parent dir (dirname) file descriptor in pdfd. |
199 | | It can be -1 even if there was no error! |
200 | | |
201 | | This is used for symlinked tables for DATA/INDEX DIRECTORY. |
202 | | The paths there have been realpath()-ed. So, we can assume here that |
203 | | |
204 | | * `pathname` is an absolute path |
205 | | * no '.', '..', and '//' in the path |
206 | | * file exists |
207 | | */ |
208 | | |
209 | | const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd) |
210 | 0 | { |
211 | 0 | char buf[FN_REFLEN + 1]; |
212 | 0 | char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf)); |
213 | 0 | int fd, dfd= -1; |
214 | |
|
215 | 0 | if (*end) |
216 | 0 | { |
217 | 0 | errno= ENAMETOOLONG; |
218 | 0 | return NULL; |
219 | 0 | } |
220 | | |
221 | 0 | if (*s != '/') /* not an absolute path */ |
222 | 0 | { |
223 | 0 | errno= ENOENT; |
224 | 0 | return NULL; |
225 | 0 | } |
226 | | |
227 | 0 | for (;;) |
228 | 0 | { |
229 | 0 | if (*e == '/') /* '//' in the path */ |
230 | 0 | { |
231 | 0 | errno= ENOENT; |
232 | 0 | goto err; |
233 | 0 | } |
234 | 0 | while (*e && *e != '/') |
235 | 0 | e++; |
236 | 0 | *e= 0; |
237 | |
|
238 | 0 | if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3)) |
239 | 0 | { |
240 | 0 | errno= ENOENT; |
241 | 0 | goto err; |
242 | 0 | } |
243 | | |
244 | 0 | if (++e >= end) |
245 | 0 | { |
246 | 0 | *pdfd= dfd; |
247 | 0 | return pathname + (s - buf); |
248 | 0 | } |
249 | | |
250 | 0 | fd = openat(dfd, s, O_NOFOLLOW | O_PATH | O_CLOEXEC); |
251 | 0 | if (fd < 0) |
252 | 0 | goto err; |
253 | | |
254 | 0 | if (dfd >= 0) |
255 | 0 | close(dfd); |
256 | |
|
257 | 0 | dfd= fd; |
258 | 0 | s= e; |
259 | 0 | } |
260 | 0 | err: |
261 | 0 | if (dfd >= 0) |
262 | 0 | close(dfd); |
263 | 0 | return NULL; |
264 | 0 | } |
265 | | #endif |