Coverage Report

Created: 2025-06-13 06:43

/src/php-src/ext/standard/link.c
Line
Count
Source (jump to first uncovered line)
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
#include <errno.h>
34
35
#include "php_string.h"
36
37
#ifndef VOLUME_NAME_NT
38
#define VOLUME_NAME_NT 0x2
39
#endif
40
41
#ifndef VOLUME_NAME_DOS
42
#define VOLUME_NAME_DOS 0x0
43
#endif
44
45
/* {{{ Return the target of a symbolic link */
46
PHP_FUNCTION(readlink)
47
0
{
48
0
  char *link;
49
0
  size_t link_len;
50
0
  char buff[MAXPATHLEN];
51
0
  ssize_t ret;
52
53
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
54
0
    Z_PARAM_PATH(link, link_len)
55
0
  ZEND_PARSE_PARAMETERS_END();
56
57
0
  if (php_check_open_basedir(link)) {
58
0
    RETURN_FALSE;
59
0
  }
60
61
0
  ret = php_sys_readlink(link, buff, MAXPATHLEN-1);
62
63
0
  if (ret == -1) {
64
#ifdef PHP_WIN32
65
    php_error_docref(NULL, E_WARNING, "readlink failed to read the symbolic link (%s), error %ld", link, GetLastError());
66
#else
67
0
    php_error_docref(NULL, E_WARNING, "%s", strerror(errno));
68
0
#endif
69
0
    RETURN_FALSE;
70
0
  }
71
  /* Append NULL to the end of the string */
72
0
  buff[ret] = '\0';
73
74
0
  RETURN_STRINGL(buff, ret);
75
0
}
76
/* }}} */
77
78
/* {{{ Returns the st_dev field of the UNIX C stat structure describing the link */
79
PHP_FUNCTION(linkinfo)
80
0
{
81
0
  char *link;
82
0
  char *dirname;
83
0
  size_t link_len;
84
0
  zend_stat_t sb = {0};
85
0
  int ret;
86
87
0
  ZEND_PARSE_PARAMETERS_START(1, 1)
88
0
    Z_PARAM_PATH(link, link_len)
89
0
  ZEND_PARSE_PARAMETERS_END();
90
91
  // TODO Check for empty string
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