198b9484cSchristos /* Libiberty realpath. Like realpath, but more consistent behavior. 298b9484cSchristos Based on gdb_realpath from GDB. 398b9484cSchristos 4*7e120ff0Schristos Copyright (C) 2003-2024 Free Software Foundation, Inc. 598b9484cSchristos 698b9484cSchristos This file is part of the libiberty library. 798b9484cSchristos 898b9484cSchristos This program is free software; you can redistribute it and/or modify 998b9484cSchristos it under the terms of the GNU General Public License as published by 1098b9484cSchristos the Free Software Foundation; either version 2 of the License, or 1198b9484cSchristos (at your option) any later version. 1298b9484cSchristos 1398b9484cSchristos This program is distributed in the hope that it will be useful, 1498b9484cSchristos but WITHOUT ANY WARRANTY; without even the implied warranty of 1598b9484cSchristos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1698b9484cSchristos GNU General Public License for more details. 1798b9484cSchristos 1898b9484cSchristos You should have received a copy of the GNU General Public License 1998b9484cSchristos along with this program; if not, write to the Free Software 2098b9484cSchristos Foundation, Inc., 51 Franklin Street - Fifth Floor, 2198b9484cSchristos Boston, MA 02110-1301, USA. */ 2298b9484cSchristos 2398b9484cSchristos /* 2498b9484cSchristos 2598b9484cSchristos @deftypefn Replacement {const char*} lrealpath (const char *@var{name}) 2698b9484cSchristos 2798b9484cSchristos Given a pointer to a string containing a pathname, returns a canonical 2898b9484cSchristos version of the filename. Symlinks will be resolved, and ``.'' and ``..'' 2998b9484cSchristos components will be simplified. The returned value will be allocated using 3098b9484cSchristos @code{malloc}, or @code{NULL} will be returned on a memory allocation error. 3198b9484cSchristos 3298b9484cSchristos @end deftypefn 3398b9484cSchristos 3498b9484cSchristos */ 3598b9484cSchristos 3698b9484cSchristos #include "config.h" 3798b9484cSchristos #include "ansidecl.h" 3898b9484cSchristos #include "libiberty.h" 3998b9484cSchristos 4098b9484cSchristos #ifdef HAVE_LIMITS_H 4198b9484cSchristos #include <limits.h> 4298b9484cSchristos #endif 4398b9484cSchristos #ifdef HAVE_STDLIB_H 4498b9484cSchristos #include <stdlib.h> 4598b9484cSchristos #endif 4698b9484cSchristos #ifdef HAVE_UNISTD_H 4798b9484cSchristos #include <unistd.h> 4898b9484cSchristos #endif 4998b9484cSchristos #ifdef HAVE_STRING_H 5098b9484cSchristos #include <string.h> 5198b9484cSchristos #endif 5298b9484cSchristos 5398b9484cSchristos /* On GNU libc systems the declaration is only visible with _GNU_SOURCE. */ 5498b9484cSchristos #if defined(HAVE_CANONICALIZE_FILE_NAME) \ 5598b9484cSchristos && defined(NEED_DECLARATION_CANONICALIZE_FILE_NAME) 5698b9484cSchristos extern char *canonicalize_file_name (const char *); 5798b9484cSchristos #endif 5898b9484cSchristos 5998b9484cSchristos #if defined(HAVE_REALPATH) 6098b9484cSchristos # if defined (PATH_MAX) 6198b9484cSchristos # define REALPATH_LIMIT PATH_MAX 6298b9484cSchristos # else 6398b9484cSchristos # if defined (MAXPATHLEN) 6498b9484cSchristos # define REALPATH_LIMIT MAXPATHLEN 6598b9484cSchristos # endif 6698b9484cSchristos # endif 6798b9484cSchristos #else 6898b9484cSchristos /* cygwin has realpath, so it won't get here. */ 6998b9484cSchristos # if defined (_WIN32) 7098b9484cSchristos # define WIN32_LEAN_AND_MEAN 71*7e120ff0Schristos # include <windows.h> /* for GetFullPathName/GetFinalPathNameByHandle/ 72*7e120ff0Schristos CreateFile/CloseHandle */ 73*7e120ff0Schristos # define WIN32_REPLACE_SLASHES(_ptr, _len) \ 74*7e120ff0Schristos for (unsigned i = 0; i != (_len); ++i) \ 75*7e120ff0Schristos if ((_ptr)[i] == '\\') (_ptr)[i] = '/'; 76*7e120ff0Schristos 77*7e120ff0Schristos # define WIN32_UNC_PREFIX "//?/UNC/" 78*7e120ff0Schristos # define WIN32_UNC_PREFIX_LEN (sizeof(WIN32_UNC_PREFIX)-1) 79*7e120ff0Schristos # define WIN32_IS_UNC_PREFIX(ptr) \ 80*7e120ff0Schristos (0 == memcmp(ptr, WIN32_UNC_PREFIX, WIN32_UNC_PREFIX_LEN)) 81*7e120ff0Schristos 82*7e120ff0Schristos # define WIN32_NON_UNC_PREFIX "//?/" 83*7e120ff0Schristos # define WIN32_NON_UNC_PREFIX_LEN (sizeof(WIN32_NON_UNC_PREFIX)-1) 84*7e120ff0Schristos # define WIN32_IS_NON_UNC_PREFIX(ptr) \ 85*7e120ff0Schristos (0 == memcmp(ptr, WIN32_NON_UNC_PREFIX, WIN32_NON_UNC_PREFIX_LEN)) 86*7e120ff0Schristos 87*7e120ff0Schristos /* Get full path name without symlinks resolution. 88*7e120ff0Schristos It also converts all forward slashes to back slashes. 89*7e120ff0Schristos */ 90*7e120ff0Schristos char* get_full_path_name(const char *filename) { 91*7e120ff0Schristos DWORD len; 92*7e120ff0Schristos char *buf, *ptr, *res; 93*7e120ff0Schristos 94*7e120ff0Schristos /* determining the required buffer size. 95*7e120ff0Schristos from the man: `If the lpBuffer buffer is too small to contain 96*7e120ff0Schristos the path, the return value is the size, in TCHARs, of the buffer 97*7e120ff0Schristos that is required to hold the path _and_the_terminating_null_character_` 98*7e120ff0Schristos */ 99*7e120ff0Schristos len = GetFullPathName(filename, 0, NULL, NULL); 100*7e120ff0Schristos 101*7e120ff0Schristos if ( len == 0 ) 102*7e120ff0Schristos return strdup(filename); 103*7e120ff0Schristos 104*7e120ff0Schristos buf = (char *)malloc(len); 105*7e120ff0Schristos 106*7e120ff0Schristos /* no point to check the result again */ 107*7e120ff0Schristos len = GetFullPathName(filename, len, buf, NULL); 108*7e120ff0Schristos buf[len] = 0; 109*7e120ff0Schristos 110*7e120ff0Schristos /* replace slashes */ 111*7e120ff0Schristos WIN32_REPLACE_SLASHES(buf, len); 112*7e120ff0Schristos 113*7e120ff0Schristos /* calculate offset based on prefix type */ 114*7e120ff0Schristos len = WIN32_IS_UNC_PREFIX(buf) 115*7e120ff0Schristos ? (WIN32_UNC_PREFIX_LEN - 2) 116*7e120ff0Schristos : WIN32_IS_NON_UNC_PREFIX(buf) 117*7e120ff0Schristos ? WIN32_NON_UNC_PREFIX_LEN 118*7e120ff0Schristos : 0 119*7e120ff0Schristos ; 120*7e120ff0Schristos 121*7e120ff0Schristos ptr = buf + len; 122*7e120ff0Schristos if ( WIN32_IS_UNC_PREFIX(buf) ) { 123*7e120ff0Schristos ptr[0] = '/'; 124*7e120ff0Schristos ptr[1] = '/'; 125*7e120ff0Schristos } 126*7e120ff0Schristos 127*7e120ff0Schristos res = strdup(ptr); 128*7e120ff0Schristos 129*7e120ff0Schristos free(buf); 130*7e120ff0Schristos 131*7e120ff0Schristos return res; 132*7e120ff0Schristos } 133*7e120ff0Schristos 134*7e120ff0Schristos # if _WIN32_WINNT >= 0x0600 135*7e120ff0Schristos 136*7e120ff0Schristos /* Get full path name WITH symlinks resolution. 137*7e120ff0Schristos It also converts all forward slashes to back slashes. 138*7e120ff0Schristos */ 139*7e120ff0Schristos char* get_final_path_name(HANDLE fh) { 140*7e120ff0Schristos DWORD len; 141*7e120ff0Schristos char *buf, *ptr, *res; 142*7e120ff0Schristos 143*7e120ff0Schristos /* determining the required buffer size. 144*7e120ff0Schristos from the man: `If the function fails because lpszFilePath is too 145*7e120ff0Schristos small to hold the string plus the terminating null character, 146*7e120ff0Schristos the return value is the required buffer size, in TCHARs. This 147*7e120ff0Schristos value _includes_the_size_of_the_terminating_null_character_`. 148*7e120ff0Schristos but in my testcase I have path with 26 chars, the function 149*7e120ff0Schristos returns 26 also, ie without the trailing zero-char... 150*7e120ff0Schristos */ 151*7e120ff0Schristos len = GetFinalPathNameByHandle( 152*7e120ff0Schristos fh 153*7e120ff0Schristos ,NULL 154*7e120ff0Schristos ,0 155*7e120ff0Schristos ,FILE_NAME_NORMALIZED | VOLUME_NAME_DOS 156*7e120ff0Schristos ); 157*7e120ff0Schristos 158*7e120ff0Schristos if ( len == 0 ) 159*7e120ff0Schristos return NULL; 160*7e120ff0Schristos 161*7e120ff0Schristos len += 1; /* for zero-char */ 162*7e120ff0Schristos buf = (char *)malloc(len); 163*7e120ff0Schristos 164*7e120ff0Schristos /* no point to check the result again */ 165*7e120ff0Schristos len = GetFinalPathNameByHandle( 166*7e120ff0Schristos fh 167*7e120ff0Schristos ,buf 168*7e120ff0Schristos ,len 169*7e120ff0Schristos ,FILE_NAME_NORMALIZED | VOLUME_NAME_DOS 170*7e120ff0Schristos ); 171*7e120ff0Schristos buf[len] = 0; 172*7e120ff0Schristos 173*7e120ff0Schristos /* replace slashes */ 174*7e120ff0Schristos WIN32_REPLACE_SLASHES(buf, len); 175*7e120ff0Schristos 176*7e120ff0Schristos /* calculate offset based on prefix type */ 177*7e120ff0Schristos len = WIN32_IS_UNC_PREFIX(buf) 178*7e120ff0Schristos ? (WIN32_UNC_PREFIX_LEN - 2) 179*7e120ff0Schristos : WIN32_IS_NON_UNC_PREFIX(buf) 180*7e120ff0Schristos ? WIN32_NON_UNC_PREFIX_LEN 181*7e120ff0Schristos : 0 182*7e120ff0Schristos ; 183*7e120ff0Schristos 184*7e120ff0Schristos ptr = buf + len; 185*7e120ff0Schristos if ( WIN32_IS_UNC_PREFIX(buf) ) { 186*7e120ff0Schristos ptr[0] = '/'; 187*7e120ff0Schristos ptr[1] = '/'; 188*7e120ff0Schristos } 189*7e120ff0Schristos 190*7e120ff0Schristos res = strdup(ptr); 191*7e120ff0Schristos 192*7e120ff0Schristos free(buf); 193*7e120ff0Schristos 194*7e120ff0Schristos return res; 195*7e120ff0Schristos } 196*7e120ff0Schristos 197*7e120ff0Schristos # endif // _WIN32_WINNT >= 0x0600 198*7e120ff0Schristos 199*7e120ff0Schristos # endif // _WIN32 20098b9484cSchristos #endif 20198b9484cSchristos 20298b9484cSchristos char * 20398b9484cSchristos lrealpath (const char *filename) 20498b9484cSchristos { 20598b9484cSchristos /* Method 1: The system has a compile time upper bound on a filename 20698b9484cSchristos path. Use that and realpath() to canonicalize the name. This is 20798b9484cSchristos the most common case. Note that, if there isn't a compile time 20898b9484cSchristos upper bound, you want to avoid realpath() at all costs. */ 20998b9484cSchristos #if defined(REALPATH_LIMIT) 21098b9484cSchristos { 21198b9484cSchristos char buf[REALPATH_LIMIT]; 21298b9484cSchristos const char *rp = realpath (filename, buf); 21398b9484cSchristos if (rp == NULL) 21498b9484cSchristos rp = filename; 21598b9484cSchristos return strdup (rp); 21698b9484cSchristos } 21798b9484cSchristos #endif /* REALPATH_LIMIT */ 21898b9484cSchristos 21998b9484cSchristos /* Method 2: The host system (i.e., GNU) has the function 22098b9484cSchristos canonicalize_file_name() which malloc's a chunk of memory and 22198b9484cSchristos returns that, use that. */ 22298b9484cSchristos #if defined(HAVE_CANONICALIZE_FILE_NAME) 22398b9484cSchristos { 22498b9484cSchristos char *rp = canonicalize_file_name (filename); 22598b9484cSchristos if (rp == NULL) 22698b9484cSchristos return strdup (filename); 22798b9484cSchristos else 22898b9484cSchristos return rp; 22998b9484cSchristos } 23098b9484cSchristos #endif 23198b9484cSchristos 23298b9484cSchristos /* Method 3: Now we're getting desperate! The system doesn't have a 23398b9484cSchristos compile time buffer size and no alternative function. Query the 23498b9484cSchristos OS, using pathconf(), for the buffer limit. Care is needed 23598b9484cSchristos though, some systems do not limit PATH_MAX (return -1 for 23698b9484cSchristos pathconf()) making it impossible to pass a correctly sized buffer 23798b9484cSchristos to realpath() (it could always overflow). On those systems, we 23898b9484cSchristos skip this. */ 23998b9484cSchristos #if defined (HAVE_REALPATH) && defined (HAVE_UNISTD_H) 24098b9484cSchristos { 24198b9484cSchristos /* Find out the max path size. */ 24298b9484cSchristos long path_max = pathconf ("/", _PC_PATH_MAX); 24398b9484cSchristos if (path_max > 0) 24498b9484cSchristos { 24598b9484cSchristos /* PATH_MAX is bounded. */ 24698b9484cSchristos char *buf, *rp, *ret; 24798b9484cSchristos buf = (char *) malloc (path_max); 24898b9484cSchristos if (buf == NULL) 24998b9484cSchristos return NULL; 25098b9484cSchristos rp = realpath (filename, buf); 25198b9484cSchristos ret = strdup (rp ? rp : filename); 25298b9484cSchristos free (buf); 25398b9484cSchristos return ret; 25498b9484cSchristos } 25598b9484cSchristos } 25698b9484cSchristos #endif 25798b9484cSchristos 258*7e120ff0Schristos /* The MS Windows method */ 25998b9484cSchristos #if defined (_WIN32) 26098b9484cSchristos { 261*7e120ff0Schristos char *res; 26298b9484cSchristos 263*7e120ff0Schristos /* For Windows Vista and greater */ 264*7e120ff0Schristos #if _WIN32_WINNT >= 0x0600 265*7e120ff0Schristos 266*7e120ff0Schristos /* For some reason the function receives just empty `filename`, but not NULL. 267*7e120ff0Schristos What should we do in that case? 268*7e120ff0Schristos According to `strdup()` implementation 269*7e120ff0Schristos (https://elixir.bootlin.com/glibc/latest/source/string/strdup.c) 270*7e120ff0Schristos it will alloc 1 byte even for empty but non NULL string. 271*7e120ff0Schristos OK, will use `strdup()` for that case. 272*7e120ff0Schristos */ 273*7e120ff0Schristos if ( 0 == strlen(filename) ) 27498b9484cSchristos return strdup(filename); 275*7e120ff0Schristos 276*7e120ff0Schristos HANDLE fh = CreateFile( 277*7e120ff0Schristos filename 278*7e120ff0Schristos ,FILE_READ_ATTRIBUTES 279*7e120ff0Schristos ,FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE 280*7e120ff0Schristos ,NULL 281*7e120ff0Schristos ,OPEN_EXISTING 282*7e120ff0Schristos ,FILE_FLAG_BACKUP_SEMANTICS 283*7e120ff0Schristos ,NULL 284*7e120ff0Schristos ); 285*7e120ff0Schristos 286*7e120ff0Schristos if ( fh == INVALID_HANDLE_VALUE ) { 287*7e120ff0Schristos res = get_full_path_name(filename); 288*7e120ff0Schristos } else { 289*7e120ff0Schristos res = get_final_path_name(fh); 290*7e120ff0Schristos CloseHandle(fh); 291*7e120ff0Schristos 292*7e120ff0Schristos if ( !res ) 293*7e120ff0Schristos res = get_full_path_name(filename); 294*7e120ff0Schristos } 295*7e120ff0Schristos 296*7e120ff0Schristos #else 297*7e120ff0Schristos 298*7e120ff0Schristos /* For Windows XP */ 299*7e120ff0Schristos res = get_full_path_name(filename); 300*7e120ff0Schristos 301*7e120ff0Schristos #endif // _WIN32_WINNT >= 0x0600 302*7e120ff0Schristos 303*7e120ff0Schristos return res; 304*7e120ff0Schristos } 305*7e120ff0Schristos #endif // _WIN32 30698b9484cSchristos } 307