1 /*
2 +----------------------------------------------------------------------+
3 | Copyright (c) The PHP Group |
4 +----------------------------------------------------------------------+
5 | This source file is subject to version 3.01 of the PHP license, |
6 | that is bundled with this package in the file LICENSE, and is |
7 | available through the world-wide-web at the following url: |
8 | https://www.php.net/license/3_01.txt |
9 | If you did not receive a copy of the PHP license and are unable to |
10 | obtain it through the world-wide-web, please send a note to |
11 | license@php.net so we can mail you a copy immediately. |
12 +----------------------------------------------------------------------+
13 | Author: |
14 +----------------------------------------------------------------------+
15 */
16
17 #include "php.h"
18
19 #if defined(HAVE_SYMLINK) || defined(PHP_WIN32)
20
21 #ifdef PHP_WIN32
22 #include <WinBase.h>
23 #endif
24
25 #include <stdlib.h>
26 #ifdef HAVE_UNISTD_H
27 #include <unistd.h>
28 #endif
29 #ifndef PHP_WIN32
30 #include <sys/stat.h>
31 #endif
32 #include <string.h>
33 #ifdef HAVE_PWD_H
34 #ifdef PHP_WIN32
35 #include "win32/pwd.h"
36 #else
37 #include <pwd.h>
38 #endif
39 #endif
40 #ifdef HAVE_GRP_H
41 # include <grp.h>
42 #endif
43 #include <errno.h>
44 #include <ctype.h>
45
46 #include "php_string.h"
47
48 #ifndef VOLUME_NAME_NT
49 #define VOLUME_NAME_NT 0x2
50 #endif
51
52 #ifndef VOLUME_NAME_DOS
53 #define VOLUME_NAME_DOS 0x0
54 #endif
55
56 /* {{{ Return the target of a symbolic link */
PHP_FUNCTION(readlink)57 PHP_FUNCTION(readlink)
58 {
59 char *link;
60 size_t link_len;
61 char buff[MAXPATHLEN];
62 ssize_t ret;
63
64 ZEND_PARSE_PARAMETERS_START(1, 1)
65 Z_PARAM_PATH(link, link_len)
66 ZEND_PARSE_PARAMETERS_END();
67
68 if (php_check_open_basedir(link)) {
69 RETURN_FALSE;
70 }
71
72 ret = php_sys_readlink(link, buff, MAXPATHLEN-1);
73
74 if (ret == -1) {
75 #ifdef PHP_WIN32
76 php_error_docref(NULL, E_WARNING, "readlink failed to read the symbolic link (%s), error %d", link, GetLastError());
77 #else
78 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
79 #endif
80 RETURN_FALSE;
81 }
82 /* Append NULL to the end of the string */
83 buff[ret] = '\0';
84
85 RETURN_STRINGL(buff, ret);
86 }
87 /* }}} */
88
89 /* {{{ Returns the st_dev field of the UNIX C stat structure describing the link */
PHP_FUNCTION(linkinfo)90 PHP_FUNCTION(linkinfo)
91 {
92 char *link;
93 char *dirname;
94 size_t link_len;
95 zend_stat_t sb = {0};
96 int ret;
97
98 ZEND_PARSE_PARAMETERS_START(1, 1)
99 Z_PARAM_PATH(link, link_len)
100 ZEND_PARSE_PARAMETERS_END();
101
102 // TODO Check for empty string
103 dirname = estrndup(link, link_len);
104 zend_dirname(dirname, link_len);
105
106 if (php_check_open_basedir(dirname)) {
107 efree(dirname);
108 RETURN_FALSE;
109 }
110
111 ret = VCWD_LSTAT(link, &sb);
112 if (ret == -1) {
113 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
114 efree(dirname);
115 RETURN_LONG(Z_L(-1));
116 }
117
118 efree(dirname);
119 RETURN_LONG((zend_long) sb.st_dev);
120 }
121 /* }}} */
122
123 /* {{{ Create a symbolic link */
PHP_FUNCTION(symlink)124 PHP_FUNCTION(symlink)
125 {
126 char *topath, *frompath;
127 size_t topath_len, frompath_len;
128 int ret;
129 char source_p[MAXPATHLEN];
130 char dest_p[MAXPATHLEN];
131 char dirname[MAXPATHLEN];
132 size_t len;
133
134 ZEND_PARSE_PARAMETERS_START(2, 2)
135 Z_PARAM_PATH(topath, topath_len)
136 Z_PARAM_PATH(frompath, frompath_len)
137 ZEND_PARSE_PARAMETERS_END();
138
139 if (!expand_filepath(frompath, source_p)) {
140 php_error_docref(NULL, E_WARNING, "No such file or directory");
141 RETURN_FALSE;
142 }
143
144 memcpy(dirname, source_p, sizeof(source_p));
145 len = zend_dirname(dirname, strlen(dirname));
146
147 if (!expand_filepath_ex(topath, dest_p, dirname, len)) {
148 php_error_docref(NULL, E_WARNING, "No such file or directory");
149 RETURN_FALSE;
150 }
151
152 if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) ||
153 php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) )
154 {
155 php_error_docref(NULL, E_WARNING, "Unable to symlink to a URL");
156 RETURN_FALSE;
157 }
158
159 if (php_check_open_basedir(dest_p)) {
160 RETURN_FALSE;
161 }
162
163 if (php_check_open_basedir(source_p)) {
164 RETURN_FALSE;
165 }
166
167 /* For the source, an expanded path must be used (in ZTS an other thread could have changed the CWD).
168 * For the target the exact string given by the user must be used, relative or not, existing or not.
169 * The target is relative to the link itself, not to the CWD. */
170 ret = php_sys_symlink(topath, source_p);
171
172 if (ret == -1) {
173 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
174 RETURN_FALSE;
175 }
176
177 RETURN_TRUE;
178 }
179 /* }}} */
180
181 /* {{{ Create a hard link */
PHP_FUNCTION(link)182 PHP_FUNCTION(link)
183 {
184 char *topath, *frompath;
185 size_t topath_len, frompath_len;
186 int ret;
187 char source_p[MAXPATHLEN];
188 char dest_p[MAXPATHLEN];
189
190 ZEND_PARSE_PARAMETERS_START(2, 2)
191 Z_PARAM_PATH(topath, topath_len)
192 Z_PARAM_PATH(frompath, frompath_len)
193 ZEND_PARSE_PARAMETERS_END();
194
195 if (!expand_filepath(frompath, source_p) || !expand_filepath(topath, dest_p)) {
196 php_error_docref(NULL, E_WARNING, "No such file or directory");
197 RETURN_FALSE;
198 }
199
200 if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) ||
201 php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY) )
202 {
203 php_error_docref(NULL, E_WARNING, "Unable to link to a URL");
204 RETURN_FALSE;
205 }
206
207 if (php_check_open_basedir(dest_p)) {
208 RETURN_FALSE;
209 }
210
211 if (php_check_open_basedir(source_p)) {
212 RETURN_FALSE;
213 }
214
215 #ifndef ZTS
216 ret = php_sys_link(topath, frompath);
217 #else
218 ret = php_sys_link(dest_p, source_p);
219 #endif
220 if (ret == -1) {
221 php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
222 RETURN_FALSE;
223 }
224
225 RETURN_TRUE;
226 }
227 /* }}} */
228
229 #endif
230