nuttx/libs/libc/stdlib/lib_realpath.c
zhengyu16 ebfe22bfb9 fs: rename PSEUDOFS_SOFTLINKS to FS_LINKS
The link support is no longer limited to the pseudo file system and now
covers both soft (symbolic) links and hard links across the VFS.  Rename
the configuration option PSEUDOFS_SOFTLINKS to the more accurate FS_LINKS
and update all references in the source, headers, Kconfig, documentation
and board defconfigs accordingly.

This is a configuration rename; any out-of-tree defconfig that still
selects PSEUDOFS_SOFTLINKS must be updated to FS_LINKS.

Signed-off-by: zhengyu16 <zhengyu16@xiaomi.com>
2026-08-27 01:12:33 +08:00

283 lines
5.4 KiB
C

/****************************************************************************
* libs/libc/stdlib/lib_realpath.c
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership. The
* ASF licenses this file to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the
* License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*
****************************************************************************/
/****************************************************************************
* Included Files
****************************************************************************/
#include <sys/stat.h>
#include <errno.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "libc.h"
/****************************************************************************
* Public Functions
****************************************************************************/
FAR char *lib_realpath(FAR const char *path, FAR char *resolved,
bool notfollow)
{
#ifdef CONFIG_FS_LINKS
FAR char *wbuf[2] =
{
};
int nlnk = 0;
int idx = 0;
ssize_t n;
#endif
FAR const char *q;
FAR char *fres = NULL;
FAR char *p;
struct stat sb;
size_t len;
if (path == NULL)
{
set_errno(EINVAL);
return NULL;
}
if (*path == '\0')
{
set_errno(ENOENT);
return NULL;
}
if (resolved == NULL)
{
fres = resolved = lib_malloc(PATH_MAX);
if (resolved == NULL)
{
set_errno(ENOMEM);
return NULL;
}
}
/* Build real path one by one with paying an attention to .,
* .. and symbolic link.
*/
/* `p' is where we'll put a new component with prepending
* a delimiter.
*/
p = resolved;
/* If relative path, start from current working directory. */
if (*path != '/')
{
/* check for resolved pointer to appease coverity */
if (getcwd(resolved, PATH_MAX) == NULL)
{
goto out;
}
len = strlen(resolved);
if (len > 1)
{
p += len;
}
}
loop:
/* Skip any slash. */
while (*path == '/')
{
path++;
}
if (*path == '\0')
{
if (p == resolved)
{
*p++ = '/';
}
*p = '\0';
#ifdef CONFIG_FS_LINKS
if (wbuf[0] != NULL)
{
lib_free(wbuf[0]);
}
#endif
return resolved;
}
/* Find the end of this component. */
q = path;
do
{
q++;
}
while (*q != '/' && *q != '\0');
/* Test . or .. */
if (path[0] == '.')
{
if (q - path == 1)
{
path = q;
goto loop;
}
if (path[1] == '.' && q - path == 2)
{
/* Trim the last component. */
if (p != resolved)
{
while (*--p != '/')
{
continue;
}
}
path = q;
goto loop;
}
}
/* Append this component. */
if (p - resolved + 1 + q - path + 1 > PATH_MAX)
{
set_errno(ENAMETOOLONG);
goto out;
}
p[0] = '/';
memcpy(&p[1], path, q - path);
p[1 + q - path] = '\0';
if (notfollow)
{
p += 1 + q - path;
path = q;
goto loop;
}
/* If this component is a symlink, toss it and prepend link
* target to unresolved path.
*/
if (lstat(resolved, &sb) == -1)
{
goto out;
}
#ifdef CONFIG_FS_LINKS
if (S_ISLNK(sb.st_mode))
{
if (nlnk++ >= SYMLOOP_MAX)
{
set_errno(ELOOP);
goto out;
}
if (wbuf[0] == NULL)
{
wbuf[0] = lib_calloc(2, PATH_MAX);
if (wbuf[0] == NULL)
{
set_errno(ENOMEM);
goto out;
}
wbuf[1] = wbuf[0] + PATH_MAX;
}
n = readlink(resolved, wbuf[idx], PATH_MAX - 1);
if (n <= 0)
{
if (n == 0)
{
set_errno(ENOENT);
}
goto out;
}
/* Append unresolved path to link target and switch to it. */
if (n + (len = strlen(q)) + 1 > PATH_MAX)
{
set_errno(ENAMETOOLONG);
goto out;
}
memcpy(&wbuf[idx][n], q, len + 1);
path = wbuf[idx];
idx ^= 1;
/* If absolute symlink, start from root. */
if (*path == '/')
{
p = resolved;
}
goto loop;
}
#endif
if (*q == '/' && !S_ISDIR(sb.st_mode))
{
set_errno(ENOTDIR);
goto out;
}
/* Advance both resolved and unresolved path. */
p += 1 + q - path;
path = q;
goto loop;
out:
lib_free(fres);
#ifdef CONFIG_FS_LINKS
if (wbuf[0] != NULL)
{
lib_free(wbuf[0]);
}
#endif
return NULL;
}
FAR char *realpath(FAR const char *path, FAR char *resolved)
{
return lib_realpath(path, resolved, false);
}