@@ -799,6 +799,74 @@ int Settings_write(const Settings* this, bool onCrash) {
799799 return r ;
800800}
801801
802+ static void Settings_resolveSymlink (char * * resolvedPath , const char * path ) {
803+ int fd = -1 ;
804+ int openFlags = O_NOCTTY | O_NOFOLLOW | O_NONBLOCK ;
805+ #ifdef O_EXEC
806+ openFlags |= O_EXEC ;
807+ #else
808+ // O_EXEC is not supported in Linux.
809+ openFlags |= O_RDONLY ;
810+ #endif
811+ #ifdef O_PATH
812+ // O_PATH is specific to Linux and FreeBSD.
813+ openFlags |= O_PATH ;
814+ #endif
815+ do {
816+ fd = open (path , openFlags );
817+ } while (fd < 0 && errno == EINTR );
818+
819+ if (fd < 0 )
820+ goto noPath ;
821+
822+ struct stat sb ;
823+ int err = fstat (fd , & sb );
824+ if (err )
825+ goto fileBroken ;
826+
827+ if (!S_ISLNK (sb .st_mode ) || (sb .st_uid != 0 && sb .st_uid != geteuid ())) {
828+ // Not a symbolic link or the symbolic link is not trusted.
829+ // Return the path to the link itself. This allows the link
830+ // target to be opened read-only when desirable.
831+ close (fd );
832+ * resolvedPath = xStrdup (path );
833+ return ;
834+ }
835+
836+ if (sb .st_size < 0 || sb .st_size >= PATH_MAX )
837+ goto fileBroken ;
838+
839+ char buf [PATH_MAX ];
840+ ssize_t len = readlinkat (fd , "" , buf , PATH_MAX );
841+ close (fd );
842+
843+ if (len < 0 || len > sb .st_size )
844+ goto noPath ;
845+
846+ buf [len ] = '\0' ;
847+ size_t dirnameLen = 0 ;
848+ if (buf [0 ] != '/' ) {
849+ const char * lastSlash = strrchr (path , '/' );
850+ dirnameLen = lastSlash ? (size_t )(lastSlash - (const char * )path + 1 ) : 0 ;
851+ }
852+ char * intermediatePath = xMalloc (dirnameLen + (size_t )len + 1 );
853+ memcpy (intermediatePath , path , dirnameLen );
854+ memcpy (intermediatePath + dirnameLen , buf , (size_t )len + 1 );
855+ char * ptr = realpath (intermediatePath , buf );
856+ free (intermediatePath );
857+ if (!ptr )
858+ goto noPath ;
859+
860+ * resolvedPath = xStrdup (buf );
861+ return ;
862+
863+ fileBroken :
864+ close (fd );
865+ noPath :
866+ * resolvedPath = xStrdup ("" );
867+ return ;
868+ }
869+
802870Settings * Settings_new (const Machine * host , Hashtable * dynamicMeters , Hashtable * dynamicColumns , Hashtable * dynamicScreens ) {
803871 Settings * this = xCalloc (1 , sizeof (Settings ));
804872
@@ -877,9 +945,7 @@ Settings* Settings_new(const Machine* host, Hashtable* dynamicMeters, Hashtable*
877945 legacyDotfile = String_cat (home , "/.htoprc" );
878946 }
879947
880- this -> filename = xMalloc (PATH_MAX );
881- if (!realpath (this -> initialFilename , this -> filename ))
882- free_and_xStrdup (& this -> filename , this -> initialFilename );
948+ Settings_resolveSymlink (& this -> filename , this -> initialFilename );
883949
884950 this -> colorScheme = 0 ;
885951#ifdef HAVE_GETMOUSE
0 commit comments