xref: /php-src/ext/standard/link.c (revision 02177848)
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