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