Coverage Report

Created: 2026-06-02 06:36

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