@@ -791,6 +791,67 @@ int Settings_write(const Settings* this, bool onCrash) {
791791 return r ;
792792}
793793
794+ static void Settings_resolveSymlink (char * * resolvedPath , const char * path ) {
795+ int fd = -1 ;
796+ int openFlags = O_NOCTTY | O_NOFOLLOW | O_NONBLOCK ;
797+ #ifdef O_EXEC
798+ openFlags |= O_EXEC ;
799+ #else
800+ // O_EXEC is not supported in Linux.
801+ openFlags |= O_RDONLY ;
802+ #endif
803+ #ifdef O_PATH
804+ // O_PATH is specific to Linux and FreeBSD.
805+ openFlags |= O_PATH ;
806+ #endif
807+ do {
808+ fd = open (path , openFlags );
809+ } while (fd < 0 && errno == EINTR );
810+
811+ if (fd < 0 )
812+ goto noPath ;
813+
814+ struct stat sb ;
815+ int err = fstat (fd , & sb );
816+ if (err )
817+ goto fileBroken ;
818+
819+ if (!S_ISLNK (sb .st_mode ) || (sb .st_uid != 0 && sb .st_uid != geteuid ())) {
820+ // Not a symbolic link or the symbolic link is not trusted.
821+ // Return the path to the link itself. This allows the link
822+ // target to be opened read-only when desirable.
823+ close (fd );
824+ * resolvedPath = xStrdup (path );
825+ return ;
826+ }
827+
828+ if (sb .st_size < 0 || sb .st_size >= PATH_MAX )
829+ goto fileBroken ;
830+
831+ char buf [PATH_MAX ];
832+ ssize_t len = readlinkat (fd , "" , buf , PATH_MAX );
833+ close (fd );
834+
835+ if (len < 0 || len > sb .st_size )
836+ goto noPath ;
837+
838+ buf [len ] = '\0' ;
839+ char * intermediatePath = xStrdup (buf );
840+ char * ptr = realpath (intermediatePath , buf );
841+ free (intermediatePath );
842+ if (!ptr )
843+ goto noPath ;
844+
845+ * resolvedPath = xStrdup (buf );
846+ return ;
847+
848+ fileBroken :
849+ close (fd );
850+ noPath :
851+ * resolvedPath = xStrdup ("" );
852+ return ;
853+ }
854+
794855Settings * Settings_new (const Machine * host , Hashtable * dynamicMeters , Hashtable * dynamicColumns , Hashtable * dynamicScreens ) {
795856 Settings * this = xCalloc (1 , sizeof (Settings ));
796857
@@ -868,9 +929,7 @@ Settings* Settings_new(const Machine* host, Hashtable* dynamicMeters, Hashtable*
868929 legacyDotfile = String_cat (home , "/.htoprc" );
869930 }
870931
871- this -> filename = xMalloc (PATH_MAX );
872- if (!realpath (this -> initialFilename , this -> filename ))
873- free_and_xStrdup (& this -> filename , this -> initialFilename );
932+ Settings_resolveSymlink (& this -> filename , this -> initialFilename );
874933
875934 this -> colorScheme = 0 ;
876935#ifdef HAVE_GETMOUSE
0 commit comments