xref: /PHP-7.2/ext/standard/link_win32.c (revision 0e6c0654)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 7                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2018 The PHP Group                                |
6    +----------------------------------------------------------------------+
7    | This source file is subject to version 3.01 of the PHP license,      |
8    | that is bundled with this package in the file LICENSE, and is        |
9    | available through the world-wide-web at the following url:           |
10    | http://www.php.net/license/3_01.txt                                  |
11    | If you did not receive a copy of the PHP license and are unable to   |
12    | obtain it through the world-wide-web, please send a note to          |
13    | license@php.net so we can mail you a copy immediately.               |
14    +----------------------------------------------------------------------+
15    | Author: Pierre A. Joye <pierre@php.net>                              |
16    +----------------------------------------------------------------------+
17  */
18 
19 /* $Id$ */
20 #ifdef PHP_WIN32
21 
22 #include "php.h"
23 #include "php_filestat.h"
24 #include "php_globals.h"
25 
26 #include <WinBase.h>
27 
28 #include <stdlib.h>
29 
30 #include <string.h>
31 #if HAVE_PWD_H
32 #include "win32/pwd.h"
33 #endif
34 
35 #if HAVE_GRP_H
36 #include "win32/grp.h"
37 #endif
38 
39 #include <errno.h>
40 #include <ctype.h>
41 
42 #include "php_link.h"
43 #include "php_string.h"
44 
45 /*
46 TODO:
47 - Create php_readlink (done), php_link and php_symlink in win32/link.c
48 - Expose them (PHPAPI) so extensions developers can use them
49 - define link/readlink/symlink to their php_ equivalent and use them in ext/standart/link.c
50 - this file is then useless and we have a portable link API
51 */
52 
53 #ifndef VOLUME_NAME_NT
54 #define VOLUME_NAME_NT 0x2
55 #endif
56 
57 #ifndef VOLUME_NAME_DOS
58 #define VOLUME_NAME_DOS 0x0
59 #endif
60 
61 /* {{{ proto string readlink(string filename)
62    Return the target of a symbolic link */
PHP_FUNCTION(readlink)63 PHP_FUNCTION(readlink)
64 {
65 	char *link;
66 	size_t link_len;
67 	char target[MAXPATHLEN];
68 
69 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &link, &link_len) == FAILURE) {
70 		return;
71 	}
72 
73 	if (OPENBASEDIR_CHECKPATH(link)) {
74 		RETURN_FALSE;
75 	}
76 
77 	if (php_sys_readlink(link, target, MAXPATHLEN) == -1) {
78 		php_error_docref(NULL, E_WARNING, "readlink failed to read the symbolic link (%s), error %d)", link, GetLastError());
79 		RETURN_FALSE;
80 	}
81 	RETURN_STRING(target);
82 }
83 /* }}} */
84 
85 /* {{{ proto int linkinfo(string filename)
86    Returns the st_dev field of the UNIX C stat structure describing the link */
PHP_FUNCTION(linkinfo)87 PHP_FUNCTION(linkinfo)
88 {
89 	char *link;
90 	char *dirname;
91 	size_t link_len;
92 	zend_stat_t sb;
93 	int ret;
94 
95 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "p", &link, &link_len) == FAILURE) {
96 		return;
97 	}
98 
99 	dirname = estrndup(link, link_len);
100 	php_dirname(dirname, link_len);
101 
102 	if (php_check_open_basedir(dirname)) {
103 		efree(dirname);
104 		RETURN_FALSE;
105 	}
106 
107 	ret = VCWD_STAT(link, &sb);
108 	if (ret == -1) {
109 		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
110 		efree(dirname);
111 		RETURN_LONG(Z_L(-1));
112 	}
113 
114 	efree(dirname);
115 	RETURN_LONG((zend_long) sb.st_dev);
116 }
117 /* }}} */
118 
119 /* {{{ proto int symlink(string target, string link)
120    Create a symbolic link */
PHP_FUNCTION(symlink)121 PHP_FUNCTION(symlink)
122 {
123 	char *topath, *frompath;
124 	size_t topath_len, frompath_len;
125 	BOOLEAN ret;
126 	char source_p[MAXPATHLEN];
127 	char dest_p[MAXPATHLEN];
128 	char dirname[MAXPATHLEN];
129 	size_t len;
130 	DWORD attr;
131 	wchar_t *dstw, *srcw;
132 
133 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &topath, &topath_len, &frompath, &frompath_len) == FAILURE) {
134 		return;
135 	}
136 
137 	if (!expand_filepath(frompath, source_p)) {
138 		php_error_docref(NULL, E_WARNING, "No such file or directory");
139 		RETURN_FALSE;
140 	}
141 
142 	memcpy(dirname, source_p, sizeof(source_p));
143 	len = php_dirname(dirname, strlen(dirname));
144 
145 	if (!expand_filepath_ex(topath, dest_p, dirname, len)) {
146 		php_error_docref(NULL, E_WARNING, "No such file or directory");
147 		RETURN_FALSE;
148 	}
149 
150 	if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) ||
151 		php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) )
152 	{
153 		php_error_docref(NULL, E_WARNING, "Unable to symlink to a URL");
154 		RETURN_FALSE;
155 	}
156 
157 	if (OPENBASEDIR_CHECKPATH(dest_p)) {
158 		RETURN_FALSE;
159 	}
160 
161 	if (OPENBASEDIR_CHECKPATH(source_p)) {
162 		RETURN_FALSE;
163 	}
164 
165 	dstw = php_win32_ioutil_any_to_w(topath);
166 	if (!dstw) {
167 		php_error_docref(NULL, E_WARNING, "UTF-16 conversion failed (error %d)", GetLastError());
168 		RETURN_FALSE;
169 	}
170 	if ((attr = GetFileAttributesW(dstw)) == INVALID_FILE_ATTRIBUTES) {
171 		free(dstw);
172 		php_error_docref(NULL, E_WARNING, "Could not fetch file information(error %d)", GetLastError());
173 		RETURN_FALSE;
174 	}
175 
176 	srcw = php_win32_ioutil_any_to_w(source_p);
177 	if (!srcw) {
178 		free(dstw);
179 		php_error_docref(NULL, E_WARNING, "UTF-16 conversion failed (error %d)", GetLastError());
180 		RETURN_FALSE;
181 	}
182 	/* For the source, an expanded path must be used (in ZTS an other thread could have changed the CWD).
183 	 * For the target the exact string given by the user must be used, relative or not, existing or not.
184 	 * The target is relative to the link itself, not to the CWD. */
185 	ret = CreateSymbolicLinkW(srcw, dstw, (attr & FILE_ATTRIBUTE_DIRECTORY ? 1 : 0));
186 
187 	if (!ret) {
188 		free(dstw);
189 		free(srcw);
190 		php_error_docref(NULL, E_WARNING, "Cannot create symlink, error code(%d)", GetLastError());
191 		RETURN_FALSE;
192 	}
193 
194 	free(dstw);
195 	free(srcw);
196 
197 	RETURN_TRUE;
198 }
199 /* }}} */
200 
201 /* {{{ proto int link(string target, string link)
202    Create a hard link */
PHP_FUNCTION(link)203 PHP_FUNCTION(link)
204 {
205 	char *topath, *frompath;
206 	size_t topath_len, frompath_len;
207 	int ret;
208 	char source_p[MAXPATHLEN];
209 	char dest_p[MAXPATHLEN];
210 	wchar_t *dstw, *srcw;
211 
212 	/*First argument to link function is the target and hence should go to frompath
213 	  Second argument to link function is the link itself and hence should go to topath */
214 	if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &frompath, &frompath_len, &topath, &topath_len) == FAILURE) {
215 		return;
216 	}
217 
218 	if (!expand_filepath(frompath, source_p) || !expand_filepath(topath, dest_p)) {
219 		php_error_docref(NULL, E_WARNING, "No such file or directory");
220 		RETURN_FALSE;
221 	}
222 
223 	if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) ||
224 		php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) )
225 	{
226 		php_error_docref(NULL, E_WARNING, "Unable to link to a URL");
227 		RETURN_FALSE;
228 	}
229 
230 	if (OPENBASEDIR_CHECKPATH(source_p)) {
231 		RETURN_FALSE;
232 	}
233 
234 	if (OPENBASEDIR_CHECKPATH(dest_p)) {
235 		RETURN_FALSE;
236 	}
237 
238 #ifndef ZTS
239 # define _TO_PATH topath
240 # define _FROM_PATH frompath
241 #else
242 # define _TO_PATH dest_p
243 # define _FROM_PATH source_p
244 #endif
245 	dstw = php_win32_ioutil_any_to_w(_TO_PATH);
246 	if (!dstw) {
247 		php_error_docref(NULL, E_WARNING, "UTF-16 conversion failed (error %d)", GetLastError());
248 		RETURN_FALSE;
249 	}
250 	srcw = php_win32_ioutil_any_to_w(_FROM_PATH);
251 	if (!srcw) {
252 		free(dstw);
253 		php_error_docref(NULL, E_WARNING, "UTF-16 conversion failed (error %d)", GetLastError());
254 		RETURN_FALSE;
255 	}
256 #undef _TO_PATH
257 #undef _FROM_PATH
258 
259 	ret = CreateHardLinkW(dstw, srcw, NULL);
260 
261 	if (ret == 0) {
262 		free(dstw);
263 		free(srcw);
264 		php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
265 		RETURN_FALSE;
266 	}
267 
268 	free(dstw);
269 	free(srcw);
270 
271 	RETURN_TRUE;
272 }
273 /* }}} */
274 
275 #endif
276 
277 /*
278  * Local variables:
279  * tab-width: 4
280  * c-basic-offset: 4
281  * End:
282  * vim600: noet sw=4 ts=4 fdm=marker
283  * vim<600: noet sw=4 ts=4
284  */
285