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