1 /*
2 +----------------------------------------------------------------------+
3 | PHP Version 7 |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 1997-2017 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 HINSTANCE kernel32;
132 typedef BOOLEAN (WINAPI *csla_func)(LPCSTR, LPCSTR, DWORD);
133 csla_func pCreateSymbolicLinkA;
134
135 kernel32 = LoadLibrary("kernel32.dll");
136
137 if (kernel32) {
138 pCreateSymbolicLinkA = (csla_func)GetProcAddress(kernel32, "CreateSymbolicLinkA");
139 if (pCreateSymbolicLinkA == NULL) {
140 php_error_docref(NULL, E_WARNING, "Can't call CreateSymbolicLinkA");
141 RETURN_FALSE;
142 }
143 } else {
144 php_error_docref(NULL, E_WARNING, "Can't call get a handle on kernel32.dll");
145 RETURN_FALSE;
146 }
147
148 if (zend_parse_parameters(ZEND_NUM_ARGS(), "pp", &topath, &topath_len, &frompath, &frompath_len) == FAILURE) {
149 return;
150 }
151
152 if (!expand_filepath(frompath, source_p)) {
153 php_error_docref(NULL, E_WARNING, "No such file or directory");
154 RETURN_FALSE;
155 }
156
157 memcpy(dirname, source_p, sizeof(source_p));
158 len = php_dirname(dirname, strlen(dirname));
159
160 if (!expand_filepath_ex(topath, dest_p, dirname, len)) {
161 php_error_docref(NULL, E_WARNING, "No such file or directory");
162 RETURN_FALSE;
163 }
164
165 if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) ||
166 php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) )
167 {
168 php_error_docref(NULL, E_WARNING, "Unable to symlink to a URL");
169 RETURN_FALSE;
170 }
171
172 if (OPENBASEDIR_CHECKPATH(dest_p)) {
173 RETURN_FALSE;
174 }
175
176 if (OPENBASEDIR_CHECKPATH(source_p)) {
177 RETURN_FALSE;
178 }
179
180 if ((attr = GetFileAttributes(topath)) == INVALID_FILE_ATTRIBUTES) {
181 php_error_docref(NULL, E_WARNING, "Could not fetch file information(error %d)", GetLastError());
182 RETURN_FALSE;
183 }
184
185 /* For the source, an expanded path must be used (in ZTS an other thread could have changed the CWD).
186 * For the target the exact string given by the user must be used, relative or not, existing or not.
187 * The target is relative to the link itself, not to the CWD. */
188 ret = pCreateSymbolicLinkA(source_p, topath, (attr & FILE_ATTRIBUTE_DIRECTORY ? 1 : 0));
189
190 if (!ret) {
191 php_error_docref(NULL, E_WARNING, "Cannot create symlink, error code(%d)", GetLastError());
192 RETURN_FALSE;
193 }
194
195 RETURN_TRUE;
196 }
197 /* }}} */
198
199 /* {{{ proto int link(string target, string link)
200 Create a hard link */
PHP_FUNCTION(link)201 PHP_FUNCTION(link)
202 {
203 char *topath, *frompath;
204 size_t topath_len, frompath_len;
205 int ret;
206 char source_p[MAXPATHLEN];
207 char dest_p[MAXPATHLEN];
208
209 /*First argument to link function is the target and hence should go to frompath
210 Second argument to link function is the link itself and hence should go to topath */
211 if (zend_parse_parameters(ZEND_NUM_ARGS(), "ss", &frompath, &frompath_len, &topath, &topath_len) == FAILURE) {
212 return;
213 }
214
215 if (!expand_filepath(frompath, source_p) || !expand_filepath(topath, dest_p)) {
216 php_error_docref(NULL, E_WARNING, "No such file or directory");
217 RETURN_FALSE;
218 }
219
220 if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) ||
221 php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) )
222 {
223 php_error_docref(NULL, E_WARNING, "Unable to link to a URL");
224 RETURN_FALSE;
225 }
226
227 if (OPENBASEDIR_CHECKPATH(source_p)) {
228 RETURN_FALSE;
229 }
230
231 if (OPENBASEDIR_CHECKPATH(dest_p)) {
232 RETURN_FALSE;
233 }
234
235 #ifndef ZTS
236 ret = CreateHardLinkA(topath, frompath, NULL);
237 #else
238 ret = CreateHardLinkA(dest_p, source_p, NULL);
239 #endif
240
241 if (ret == 0) {
242 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
243 RETURN_FALSE;
244 }
245
246 RETURN_TRUE;
247 }
248 /* }}} */
249
250 #endif
251
252 /*
253 * Local variables:
254 * tab-width: 4
255 * c-basic-offset: 4
256 * End:
257 * vim600: noet sw=4 ts=4 fdm=marker
258 * vim<600: noet sw=4 ts=4
259 */
260