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