xref: /PHP-5.3/ext/standard/link.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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:                                                              |
16    +----------------------------------------------------------------------+
17  */
18 
19 /* $Id$ */
20 
21 #include "php.h"
22 #include "php_filestat.h"
23 #include "php_globals.h"
24 
25 #ifdef HAVE_SYMLINK
26 
27 #include <stdlib.h>
28 #if HAVE_UNISTD_H
29 #include <unistd.h>
30 #endif
31 #include <sys/stat.h>
32 #include <string.h>
33 #if HAVE_PWD_H
34 #ifdef PHP_WIN32
35 #include "win32/pwd.h"
36 #else
37 #include <pwd.h>
38 #endif
39 #endif
40 #if HAVE_GRP_H
41 #ifdef PHP_WIN32
42 #include "win32/grp.h"
43 #else
44 #include <grp.h>
45 #endif
46 #endif
47 #include <errno.h>
48 #include <ctype.h>
49 
50 #include "safe_mode.h"
51 #include "php_link.h"
52 #include "php_string.h"
53 
54 /* {{{ proto string readlink(string filename)
55    Return the target of a symbolic link */
PHP_FUNCTION(readlink)56 PHP_FUNCTION(readlink)
57 {
58 	char *link;
59 	int link_len;
60 	char buff[MAXPATHLEN];
61 	int ret;
62 
63 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &link, &link_len) == FAILURE) {
64 		return;
65 	}
66 
67 	if (strlen(link) != link_len) {
68 		RETURN_FALSE;
69 	}
70 
71 	if (PG(safe_mode) && !php_checkuid(link, NULL, CHECKUID_CHECK_FILE_AND_DIR)) {
72 		RETURN_FALSE;
73 	}
74 
75 	if (php_check_open_basedir(link TSRMLS_CC)) {
76 		RETURN_FALSE;
77 	}
78 
79 	ret = php_sys_readlink(link, buff, MAXPATHLEN-1);
80 
81 	if (ret == -1) {
82 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
83 		RETURN_FALSE;
84 	}
85 	/* Append NULL to the end of the string */
86 	buff[ret] = '\0';
87 
88 	RETURN_STRING(buff, 1);
89 }
90 /* }}} */
91 
92 /* {{{ proto int linkinfo(string filename)
93    Returns the st_dev field of the UNIX C stat structure describing the link */
PHP_FUNCTION(linkinfo)94 PHP_FUNCTION(linkinfo)
95 {
96 	char *link;
97 	char *dirname;
98 	int link_len, dir_len;
99 	struct stat sb;
100 	int ret;
101 
102 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &link, &link_len) == FAILURE) {
103 		return;
104 	}
105 
106 	dirname = estrndup(link, link_len);
107 	dir_len = php_dirname(dirname, link_len);
108 
109 	if (php_check_open_basedir(dirname TSRMLS_CC)) {
110 		efree(dirname);
111 		RETURN_FALSE;
112 	}
113 
114 	ret = VCWD_LSTAT(link, &sb);
115 	if (ret == -1) {
116 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
117 		efree(dirname);
118 		RETURN_LONG(-1L);
119 	}
120 
121 	efree(dirname);
122 	RETURN_LONG((long) sb.st_dev);
123 }
124 /* }}} */
125 
126 /* {{{ proto int symlink(string target, string link)
127    Create a symbolic link */
PHP_FUNCTION(symlink)128 PHP_FUNCTION(symlink)
129 {
130 	char *topath, *frompath;
131 	int topath_len, frompath_len;
132 	int ret;
133 	char source_p[MAXPATHLEN];
134 	char dest_p[MAXPATHLEN];
135 	char dirname[MAXPATHLEN];
136 	size_t len;
137 
138 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &topath, &topath_len, &frompath, &frompath_len) == FAILURE) {
139 		return;
140 	}
141 
142 	if (strlen(topath) != topath_len) {
143 		RETURN_FALSE;
144 	}
145 
146 	if (strlen(frompath) != frompath_len) {
147 		RETURN_FALSE;
148 	}
149 
150 	if (!expand_filepath(frompath, source_p TSRMLS_CC)) {
151 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such file or directory");
152 		RETURN_FALSE;
153 	}
154 
155 	memcpy(dirname, source_p, sizeof(source_p));
156 	len = php_dirname(dirname, strlen(dirname));
157 
158 	if (!expand_filepath_ex(topath, dest_p, dirname, len TSRMLS_CC)) {
159 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such file or directory");
160 		RETURN_FALSE;
161 	}
162 
163 	if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) ||
164 		php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) )
165 	{
166 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to symlink to a URL");
167 		RETURN_FALSE;
168 	}
169 
170 	if (PG(safe_mode) && !php_checkuid(dest_p, NULL, CHECKUID_CHECK_FILE_AND_DIR)) {
171 		RETURN_FALSE;
172 	}
173 
174 	if (PG(safe_mode) && !php_checkuid(source_p, NULL, CHECKUID_CHECK_FILE_AND_DIR)) {
175 		RETURN_FALSE;
176 	}
177 
178 	if (php_check_open_basedir(dest_p TSRMLS_CC)) {
179 		RETURN_FALSE;
180 	}
181 
182 	if (php_check_open_basedir(source_p TSRMLS_CC)) {
183 		RETURN_FALSE;
184 	}
185 
186 	/* For the source, an expanded path must be used (in ZTS an other thread could have changed the CWD).
187 	 * For the target the exact string given by the user must be used, relative or not, existing or not.
188 	 * The target is relative to the link itself, not to the CWD. */
189 	ret = symlink(topath, source_p);
190 
191 	if (ret == -1) {
192 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
193 		RETURN_FALSE;
194 	}
195 
196 	RETURN_TRUE;
197 }
198 /* }}} */
199 
200 /* {{{ proto int link(string target, string link)
201    Create a hard link */
PHP_FUNCTION(link)202 PHP_FUNCTION(link)
203 {
204 	char *topath, *frompath;
205 	int topath_len, frompath_len;
206 	int ret;
207 	char source_p[MAXPATHLEN];
208 	char dest_p[MAXPATHLEN];
209 
210 	if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss", &topath, &topath_len, &frompath, &frompath_len) == FAILURE) {
211 		return;
212 	}
213 
214 	if (strlen(topath) != topath_len) {
215 		RETURN_FALSE;
216 	}
217 
218 	if (strlen(frompath) != frompath_len) {
219 		RETURN_FALSE;
220 	}
221 
222 	if (!expand_filepath(frompath, source_p TSRMLS_CC) || !expand_filepath(topath, dest_p TSRMLS_CC)) {
223 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "No such file or directory");
224 		RETURN_FALSE;
225 	}
226 
227 	if (php_stream_locate_url_wrapper(source_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) ||
228 		php_stream_locate_url_wrapper(dest_p, NULL, STREAM_LOCATE_WRAPPERS_ONLY TSRMLS_CC) )
229 	{
230 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to link to a URL");
231 		RETURN_FALSE;
232 	}
233 
234 	if (PG(safe_mode) && !php_checkuid(dest_p, NULL, CHECKUID_CHECK_FILE_AND_DIR)) {
235 		RETURN_FALSE;
236 	}
237 
238 	if (PG(safe_mode) && !php_checkuid(source_p, NULL, CHECKUID_CHECK_FILE_AND_DIR)) {
239 		RETURN_FALSE;
240 	}
241 
242 	if (php_check_open_basedir(dest_p TSRMLS_CC)) {
243 		RETURN_FALSE;
244 	}
245 
246 	if (php_check_open_basedir(source_p TSRMLS_CC)) {
247 		RETURN_FALSE;
248 	}
249 
250 #ifndef ZTS
251 	ret = link(topath, frompath);
252 #else
253 	ret = link(dest_p, source_p);
254 #endif
255 	if (ret == -1) {
256 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", strerror(errno));
257 		RETURN_FALSE;
258 	}
259 
260 	RETURN_TRUE;
261 }
262 /* }}} */
263 
264 #endif
265 
266 /*
267  * Local variables:
268  * tab-width: 4
269  * c-basic-offset: 4
270  * End:
271  * vim600: noet sw=4 ts=4 fdm=marker
272  * vim<600: noet sw=4 ts=4
273  */
274