Coverage Report

Created: 2026-02-26 06:20

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/libntp/ntp_realpath.c
Line
Count
Source
1
/*
2
 * ntp_realpath.c - get real path for a file
3
 *  Juergen Perlinger (perlinger@ntp.org) for the NTP project.
4
 *  Feb 11, 2014 for the NTP project.
5
 * 
6
 * This is a butchered version of FreeBSD's implementation of 'realpath()',
7
 * and the following copyright applies:
8
 *----------------------------------------------------------------------
9
 */
10
11
/*-
12
 * SPDX-License-Identifier: BSD-3-Clause
13
 *
14
 * Copyright (c) 2003 Constantin S. Svintsoff <kostik@iclub.nsu.ru>
15
 *
16
 * Redistribution and use in source and binary forms, with or without
17
 * modification, are permitted provided that the following conditions
18
 * are met:
19
 * 1. Redistributions of source code must retain the above copyright
20
 *    notice, this list of conditions and the following disclaimer.
21
 * 2. Redistributions in binary form must reproduce the above copyright
22
 *    notice, this list of conditions and the following disclaimer in the
23
 *    documentation and/or other materials provided with the distribution.
24
 * 3. The names of the authors may not be used to endorse or promote
25
 *    products derived from this software without specific prior written
26
 *    permission.
27
 *
28
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
29
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
30
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
31
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
32
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
33
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
34
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
35
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
36
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
37
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
38
 * SUCH DAMAGE.
39
 */
40
41
#ifdef HAVE_CONFIG_H
42
#include <config.h>
43
#endif
44
#include "ntp_stdlib.h"
45
46
/* ================================================================== */
47
#if defined(SYS_WINNT)
48
/* ================================================================== */
49
50
#include <stdlib.h>
51
52
/* On Windows, we assume 2k for a file path is enough. */
53
#define NTP_PATH_MAX  2048
54
55
static char *
56
realpath1(const char *path, char *resolved)
57
{
58
  /* Items in the device name space get passed back AS IS. Everything
59
   * else is fed through '_fullpath()', which is probably the closest
60
   * counterpart to what 'realpath()' is expected to do on Windows...
61
   */
62
  char * retval = NULL;
63
64
  if (!strncmp(path, "\\\\.\\", 4)) {
65
    if (strlcpy(resolved, path, NTP_PATH_MAX) >= NTP_PATH_MAX)
66
      errno = ENAMETOOLONG;
67
    else
68
      retval = resolved;
69
  } else if ((retval = _fullpath(resolved, path, NTP_PATH_MAX)) == NULL) {
70
    errno = ENAMETOOLONG;
71
  }
72
  return retval;
73
}
74
75
/* ================================================================== */
76
#elif !defined(HAVE_FUNC_POSIX_REALPATH)
77
/* ================================================================== */
78
79
#include <sys/stat.h>
80
#include <errno.h>
81
#include <stdlib.h>
82
#include <string.h>
83
#include <unistd.h>
84
#include <fcntl.h>
85
86
/* The following definitions are to avoid system settings with excessive
87
 * values for maxmimum path length and symlink chains/loops. Adjust with
88
 * care, if that's ever needed: some buffers are on the stack!
89
 */
90
#define NTP_PATH_MAX  1024
91
#define NTP_MAXSYMLINKS 16
92
93
/*
94
 * Find the real name of path, by removing all ".", ".." and symlink
95
 * components.  Returns (resolved) on success, or (NULL) on failure,
96
 * in which case the path which caused trouble is left in (resolved).
97
 */
98
static char *
99
realpath1(const char *path, char *resolved)
100
{
101
  struct stat sb;
102
  char *p, *q;
103
  size_t left_len, resolved_len, next_token_len;
104
  unsigned symlinks;
105
  ssize_t slen;
106
  char left[NTP_PATH_MAX], next_token[NTP_PATH_MAX], link_tgt[NTP_PATH_MAX];
107
108
  symlinks = 0;
109
  if (path[0] == '/') {
110
    resolved[0] = '/';
111
    resolved[1] = '\0';
112
    if (path[1] == '\0')
113
      return (resolved);
114
    resolved_len = 1;
115
    left_len = strlcpy(left, path + 1, sizeof(left));
116
  } else {
117
    if (getcwd(resolved, NTP_PATH_MAX) == NULL) {
118
      resolved[0] = '.';
119
      resolved[1] = '\0';
120
      return (NULL);
121
    }
122
    resolved_len = strlen(resolved);
123
    left_len = strlcpy(left, path, sizeof(left));
124
  }
125
  if (left_len >= sizeof(left) || resolved_len >= NTP_PATH_MAX) {
126
    errno = ENAMETOOLONG;
127
    return (NULL);
128
  }
129
130
  /*
131
   * Iterate over path components in `left'.
132
   */
133
  while (left_len != 0) {
134
    /*
135
     * Extract the next path component and adjust `left'
136
     * and its length.
137
     */
138
    p = strchr(left, '/');
139
140
    next_token_len = p != NULL ? (size_t)(p - left) : left_len;
141
    memcpy(next_token, left, next_token_len);
142
    next_token[next_token_len] = '\0';
143
144
    if (p != NULL) {
145
      left_len -= next_token_len + 1;
146
      memmove(left, p + 1, left_len + 1);
147
    } else {
148
      left[0] = '\0';
149
      left_len = 0;
150
    }
151
152
    if (resolved[resolved_len - 1] != '/') {
153
      if (resolved_len + 1 >= NTP_PATH_MAX) {
154
        errno = ENAMETOOLONG;
155
        return (NULL);
156
      }
157
      resolved[resolved_len++] = '/';
158
      resolved[resolved_len] = '\0';
159
    }
160
    if ('\0' == next_token[0]) {
161
      /* Handle consequential slashes. */
162
      continue;
163
    } else if (strcmp(next_token, ".") == 0) {
164
      continue;
165
    } else if (strcmp(next_token, "..") == 0) {
166
      /*
167
       * Strip the last path component except when we have
168
       * single "/"
169
       */
170
      if (resolved_len > 1) {
171
        resolved[resolved_len - 1] = '\0';
172
        q = strrchr(resolved, '/') + 1;
173
        *q = '\0';
174
        resolved_len = q - resolved;
175
      }
176
      continue;
177
    }
178
179
    /*
180
     * Append the next path component and lstat() it.
181
     */
182
    resolved_len = strlcat(resolved, next_token, NTP_PATH_MAX);
183
    if (resolved_len >= NTP_PATH_MAX) {
184
      errno = ENAMETOOLONG;
185
      return (NULL);
186
    }
187
    if (lstat(resolved, &sb) != 0)
188
      return (NULL);
189
    if (S_ISLNK(sb.st_mode)) {
190
      if (++symlinks > NTP_MAXSYMLINKS) {
191
        errno = ELOOP;
192
        return (NULL);
193
      }
194
      slen = readlink(resolved, link_tgt, sizeof(link_tgt));
195
      if (slen <= 0 || slen >= (ssize_t)sizeof(link_tgt)) {
196
        if (slen < 0) {
197
          /* keep errno from readlink(2) call */
198
        } else if (slen == 0) {
199
          errno = ENOENT;
200
        } else {
201
          errno = ENAMETOOLONG;
202
        }
203
        return (NULL);
204
      }
205
      link_tgt[slen] = '\0';
206
      if (link_tgt[0] == '/') {
207
        resolved[1] = '\0';
208
        resolved_len = 1;
209
      } else {
210
        /* Strip the last path component. */
211
        q = strrchr(resolved, '/') + 1;
212
        *q = '\0';
213
        resolved_len = q - resolved;
214
      }
215
216
      /*
217
       * If there are any path components left, then
218
       * append them to link_tgt. The result is placed
219
       * in `left'.
220
       */
221
      if (p != NULL) {
222
        if (link_tgt[slen - 1] != '/') {
223
          if (slen + 1 >= (ssize_t)sizeof(link_tgt)) {
224
            errno = ENAMETOOLONG;
225
            return (NULL);
226
          }
227
          link_tgt[slen] = '/';
228
          link_tgt[slen + 1] = 0;
229
        }
230
        left_len = strlcat(link_tgt, left,
231
               sizeof(link_tgt));
232
        if (left_len >= sizeof(link_tgt)) {
233
          errno = ENAMETOOLONG;
234
          return (NULL);
235
        }
236
      }
237
      left_len = strlcpy(left, link_tgt, sizeof(left));
238
    } else if (!S_ISDIR(sb.st_mode) && p != NULL) {
239
      errno = ENOTDIR;
240
      return (NULL);
241
    }
242
  }
243
244
  /*
245
   * Remove trailing slash except when the resolved pathname
246
   * is a single "/".
247
   */
248
  if (resolved_len > 1 && resolved[resolved_len - 1] == '/')
249
    resolved[resolved_len - 1] = '\0';
250
  return (resolved);
251
}
252
253
/* ================================================================== */
254
#endif /* !defined(SYS_WINNT) && !defined(HAVE_POSIX_REALPATH) */
255
/* ================================================================== */
256
257
char *
258
ntp_realpath(const char * path)
259
0
{
260
0
#   if defined(HAVE_FUNC_POSIX_REALPATH)
261
262
0
  return realpath(path, NULL);
263
264
#   else
265
266
  char *res = NULL, *m = NULL;
267
  if (path == NULL)
268
    errno = EINVAL;
269
  else if (path[0] == '\0')
270
    errno = ENOENT;
271
  else if ((m = malloc(NTP_PATH_MAX)) == NULL)
272
    errno = ENOMEM; /* MSVCRT malloc does not set this... */
273
  else if ((res = realpath1(path, m)) == NULL)
274
    free(m);
275
  else
276
    res = realloc(res, strlen(res) + 1);
277
  return (res);
278
279
#   endif
280
0
}