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-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
always_valid(const char * filename)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
my_readlink(char * to,const char * filename,myf MyFlags)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
my_symlink(const char * content,const char * linkname,myf MyFlags)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
my_is_symlink(const char * filename)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
my_realpath(char * to,const char * filename,myf MyFlags)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 if (MyFlags)
158 DBUG_PRINT("error",("realpath failed with errno: %d", errno));
159 my_errno=errno;
160 if (MyFlags & MY_WME)
161 my_error(EE_REALPATH, MYF(0), filename, my_errno);
162 my_load_path(to, filename, NullS);
163 if (my_errno == ENOENT)
164 result= 1;
165 else
166 result= -1;
167 }
168 DBUG_RETURN(result);
169 #elif defined(_WIN32)
170 int ret= GetFullPathName(filename,FN_REFLEN, to, NULL);
171 if (ret == 0 || ret > FN_REFLEN)
172 {
173 my_errno= (ret > FN_REFLEN) ? ENAMETOOLONG : GetLastError();
174 if (MyFlags & MY_WME)
175 my_error(EE_REALPATH, MYF(0), filename, my_errno);
176 /*
177 GetFullPathName didn't work : use my_load_path() which is a poor
178 substitute original name but will at least be able to resolve
179 paths that starts with '.'.
180 */
181 my_load_path(to, filename, NullS);
182 return -1;
183 }
184 #else
185 my_load_path(to, filename, NullS);
186 #endif
187 return 0;
188 }
189
190 #ifdef HAVE_OPEN_PARENT_DIR_NOSYMLINKS
191 /** opens the parent dir. walks the path, and does not resolve symlinks
192
193 returns the pointer to the file name (basename) within the pathname
194 or NULL in case of an error
195
196 stores the parent dir (dirname) file descriptor in pdfd.
197 It can be -1 even if there was no error!
198
199 This is used for symlinked tables for DATA/INDEX DIRECTORY.
200 The paths there have been realpath()-ed. So, we can assume here that
201
202 * `pathname` is an absolute path
203 * no '.', '..', and '//' in the path
204 * file exists
205 */
206
my_open_parent_dir_nosymlinks(const char * pathname,int * pdfd)207 const char *my_open_parent_dir_nosymlinks(const char *pathname, int *pdfd)
208 {
209 char buf[FN_REFLEN + 1];
210 char *s= buf, *e= buf+1, *end= strnmov(buf, pathname, sizeof(buf));
211 int fd, dfd= -1;
212
213 if (*end)
214 {
215 errno= ENAMETOOLONG;
216 return NULL;
217 }
218
219 if (*s != '/') /* not an absolute path */
220 {
221 errno= ENOENT;
222 return NULL;
223 }
224
225 for (;;)
226 {
227 if (*e == '/') /* '//' in the path */
228 {
229 errno= ENOENT;
230 goto err;
231 }
232 while (*e && *e != '/')
233 e++;
234 *e= 0;
235
236 if (!memcmp(s, ".", 2) || !memcmp(s, "..", 3))
237 {
238 errno= ENOENT;
239 goto err;
240 }
241
242 if (++e >= end)
243 {
244 *pdfd= dfd;
245 return pathname + (s - buf);
246 }
247
248 fd = openat(dfd, s, O_NOFOLLOW | O_PATH | O_CLOEXEC);
249 if (fd < 0)
250 goto err;
251
252 if (dfd >= 0)
253 close(dfd);
254
255 dfd= fd;
256 s= e;
257 }
258 err:
259 if (dfd >= 0)
260 close(dfd);
261 return NULL;
262 }
263 #endif
264