Repository: syncthing/syncthing (language: go)

Issue:
Panic in failure reporting
```
[start] 2022/07/31 08:25:34 INFO: syncthing v1.20.3-dev.8.gec069743-tomasz86-v1.20.2 "Fermium Flea" (go1.18.3 windows-amd64) tomasz86@tomasz86 2022-06-07 07:12:59 UTC [noupgrade, stnoupgrade]
Panic at 2022-07-31T10:24:39+09:00
panic: assignment to entry in nil map

goroutine 30 [running]:
github.com/syncthing/syncthing/lib/model.(*deadlockDetector).watchInner(0xc00019cc00, {0x1a63b64, 0x4}, 0xc000fe3440)
	github.com/syncthing/syncthing/lib/model/util.go:73 +0x22c
github.com/syncthing/syncthing/lib/model.(*deadlockDetector).Watch.func1()
	github.com/syncthing/syncthing/lib/model/util.go:57 +0x65
created by github.com/syncthing/syncthing/lib/model.(*deadlockDetector).Watch
	github.com/syncthing/syncthing/lib/model/util.go:45 +0x115
goroutine 1 [chan receive, 118 minutes]:
```

https://forum.syncthing.net/t/panic-assignment-to-entry-in-nil-map/18791


Discussion / hints:

Fix patch:
diff --git a/cmd/syncthing/main.go b/cmd/syncthing/main.go
index 1c1f38793af..3471c7b1daa 100644
--- a/cmd/syncthing/main.go
+++ b/cmd/syncthing/main.go
@@ -292,16 +292,25 @@ func (options serveOptions) Run() error {
 		os.Exit(svcutil.ExitError.AsInt())
 	}
 
-	if options.LogFile == "default" || options.LogFile == "" {
+	// Treat an explicitly empty log file name as no log file
+	if options.LogFile == "" {
+		options.LogFile = "-"
+	}
+	if options.LogFile != "default" {
 		// We must set this *after* expandLocations above.
-		// Handling an empty value is for backwards compatibility (<1.4.1).
-		options.LogFile = locations.Get(locations.LogFile)
+		if err := locations.Set(locations.LogFile, options.LogFile); err != nil {
+			l.Warnln("Setting log file path:", err)
+			os.Exit(svcutil.ExitError.AsInt())
+		}
 	}
 
-	if options.DebugGUIAssetsDir == "" {
+	if options.DebugGUIAssetsDir != "" {
 		// The asset dir is blank if STGUIASSETS wasn't set, in which case we
 		// should look for extra assets in the default place.
-		options.DebugGUIAssetsDir = locations.Get(locations.GUIAssets)
+		if err := locations.Set(locations.GUIAssets, options.DebugGUIAssetsDir); err != nil {
+			l.Warnln("Setting GUI assets path:", err)
+			os.Exit(svcutil.ExitError.AsInt())
+		}
 	}
 
 	if options.Version {
@@ -310,7 +319,7 @@ func (options serveOptions) Run() error {
 	}
 
 	if options.Paths {
-		showPaths(options)
+		fmt.Print(locations.PrettyPaths())
 		return nil
 	}
 
@@ -612,7 +621,6 @@ func syncthingMain(options serveOptions) {
 	}
 
 	appOpts := syncthing.Options{
-		AssetDir:             options.DebugGUIAssetsDir,
 		DeadlockTimeoutS:     options.DebugDeadlockTimeout,
 		NoUpgrade:            options.NoUpgrade,
 		ProfilerAddr:         options.DebugProfilerListen,
@@ -890,16 +898,6 @@ func cleanConfigDirectory() {
 	}
 }
 
-func showPaths(options serveOptions) {
-	fmt.Printf("Configuration file:\n\t%s\n\n", locations.Get(locations.ConfigFile))
-	fmt.Printf("Database directory:\n\t%s\n\n", locations.Get(locations.Database))
-	fmt.Printf("Device private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.KeyFile), locations.Get(locations.CertFile))
-	fmt.Printf("HTTPS private key & certificate files:\n\t%s\n\t%s\n\n", locations.Get(locations.HTTPSKeyFile), locations.Get(locations.HTTPSCertFile))
-	fmt.Printf("Log file:\n\t%s\n\n", options.LogFile)
-	fmt.Printf("GUI override directory:\n\t%s\n\n", options.DebugGUIAssetsDir)
-	fmt.Printf("Default sync folder directory:\n\t%s\n\n", locations.Get(locations.DefFolder))
-}
-
 func setPauseState(cfgWrapper config.Wrapper, paused bool) {
 	_, err := cfgWrapper.Modify(func(cfg *config.Configuration) {
 		for i := range cfg.Devices {
diff --git a/cmd/syncthing/monitor.go b/cmd/syncthing/monitor.go
index a118a24aebd..d15a449efc0 100644
--- a/cmd/syncthing/monitor.go
+++ b/cmd/syncthing/monitor.go
@@ -48,7 +48,7 @@ func monitorMain(options serveOptions) {
 
 	var dst io.Writer = os.Stdout
 
-	logFile := options.LogFile
+	logFile := locations.Get(locations.LogFile)
 	if logFile != "-" {
 		if expanded, err := fs.ExpandTilde(logFile); err == nil {
 			logFile = expanded
diff --git a/gui/default/assets/css/overrides.css b/gui/default/assets/css/overrides.css
index fcd8caa50d2..527c5cc59fd 100644
--- a/gui/default/assets/css/overrides.css
+++ b/gui/default/assets/css/overrides.css
@@ -420,6 +420,8 @@ ul.three-columns li, ul.two-columns li {
         height: 276px;
     }
 
+    table.table-auto td,
+    table.table-auto th,
     table.table-condensed td,
     table.table-condensed th {
         /* for mobile phones to allow linebreaks in long repro folder/shared with
diff --git a/gui/default/index.html b/gui/default/index.html
index 447472b2a32..3a463ac6ad4 100644
--- a/gui/default/index.html
+++ b/gui/default/index.html
@@ -110,7 +110,7 @@ <h3 class="panel-title">
                   <span class="fas fa-fw fa-question-circle"></span>&nbsp;<span translate>Help</span>
                 </a>
               </li>
-              <li><a href="" data-toggle="modal" data-target="#about"><span class="far fa-fw fa-heart"></span>&nbsp;<span translate>About</span></a></li>
+              <li><a href="" ng-click="about.show()"><span class="far fa-fw fa-heart"></span>&nbsp;<span translate>About</span></a></li>
               <li class="divider" aria-hidden="true"></li>
               <li><a href="" ng-click="advanced()"><span class="fas fa-fw fa-cogs"></span>&nbsp;<span translate>Advanced</span></a></li>
               <li><a href="" ng-click="logging.show()"><span class="far fa-fw fa-file-alt"></span>&nbsp;<span translate>Logs</span></a></li>
diff --git a/gui/default/syncthing/core/aboutModalView.html b/gui/default/syncthing/core/aboutModalView.html
index ef0106a3aea..06c65c234ed 100644
--- a/gui/default/syncthing/core/aboutModalView.html
+++ b/gui/default/syncthing/core/aboutModalView.html
@@ -15,61 +15,114 @@ <h1 class="text-center">
     </p>
     <p class="text-center" translate>Syncthing is Free and Open Source Software licensed as MPL v2.0.</p>
 
-    <hr />
-    <h4 class="text-center" translate>The Syncthing Authors</h4>
-    <div class="row">
-      <div class="col-md-12" id="contributor-list">
+    <ul class="nav nav-tabs">
+      <li class="active"><a data-toggle="tab" href="#about-authors" translate>Authors</a></li>
+      <li><a data-toggle="tab" href="#about-includes" translate>Included Software</a></li>
+      <li><a data-toggle="tab" href="#about-paths" translate>Paths</a></li>
+    </ul>
+    <div class="tab-content">
+
+      <div id="about-authors" class="tab-pane in active">
+        <h4 class="text-center" translate>The Syncthing Authors</h4>
+        <div class="row">
+          <div class="col-md-12" id="contributor-list">
 Jakob Borg, Audrius Butkevicius, Jesse Lucas, Simon Frei, Alexander Graf, Alexandre Viau, Anderson Mesquita, André Colomb, Antony Male, Ben Schulz, Caleb Callaway, Daniel Harte, Evgeny Kuznetsov, Lars K.W. Gohlke, Lode Hoste, Michael Ploujnikov, Nate Morrison, Philippe Schommers, Ryan Sullivan, Sergey Mishin, Stefan Tatschner, Tomasz Wilczyński, Wulf Weich, greatroar, Aaron Bieber, Adam Piggott, Adel Qalieh, Alan Pope, Alberto Donato, Alessandro G., Alex Lindeman, Alex Xu, Aman Gupta, Andrew Dunham, Andrew Meyer, Andrew Rabert, Andrey D, Anjan Momi, Antoine Lamielle, Anur, Aranjedeath, Arkadiusz Tymiński, Aroun, Arthur Axel fREW Schmidt, Artur Zubilewicz, Aurélien Rainone, BAHADIR YILMAZ, Bart De Vries, Ben Curthoys, Ben Shepherd, Ben Sidhom, Benedikt Heine, Benedikt Morbach, Benjamin Nater, Benno Fünfstück, Benny Ng, Boqin Qin, Boris Rybalkin, Brandon Philips, Brendan Long, Brian R. Becker, Carsten Hagemann, Cathryne Linenweaver, Cedric Staniewski, Chih-Hsuan Yen, Choongkyu, Chris Howie, Chris Joel, Chris Tonkinson, Christian Prescott, Colin Kennedy, Cromefire_, Cyprien Devillez, Dale Visser, Dan, Daniel Barczyk, Daniel Bergmann, Daniel Martí, Darshil Chanpura, David Rimmer, Denis A., Dennis Wilson, Devon G. Redekopp, Dmitry Saveliev, Domenic Horner, Dominik Heidler, Elias Jarlebring, Elliot Huffman, Emil Hessman, Eng Zer Jun, Eric Lesiuta, Erik Meitner, Evan Spensley, Federico Castagnini, Felix Ableitner, Felix Lampe, Felix Unterpaintner, Francois-Xavier Gsell, Frank Isemann, Gahl Saraf, Gilli Sigurdsson, Gleb Sinyavskiy, Graham Miln, Greg, Han Boetes, HansK-p, Harrison Jones, Heiko Zuerker, Hugo Locurcio, Iain Barnett, Ian Johnson, Ikko Ashimine, Ilya Brin, Iskander Sharipov, Jaakko Hannikainen, Jacek Szafarkiewicz, Jack Croft, Jacob, Jake Peterson, James Patterson, Jaroslav Lichtblau, Jaroslav Malec, Jaya Chithra, Jeffery To, Jens Diemer, Jerry Jacobs, Jochen Voss, Johan Andersson, Johan Vromans, John Rinehart, Jonas Thelemann, Jonathan, Jonathan Cross, Jonta, Jose Manuel Delicado, Jörg Thalheim, Jędrzej Kula, Kalle Laine, Karol Różycki, Kebin Liu, Keith Turner, Kelong Cong, Ken'ichi Kamada, Kevin Allen, Kevin Bushiri, Kevin White, Jr., Kurt Fitzner, LSmithx2, Lars Lehtonen, Laurent Arnoud, Laurent Etiemble, Leo Arias, Liu Siyuan, Lord Landon Agahnim, Lukas Lihotzki, Majed Abdulaziz, Marc Laporte, Marc Pujol, Marcin Dziadus, Marcus Legendre, Mario Majila, Mark Pulford, Martchus, Mateusz Naściszewski, Mateusz Ż, Matic Potočnik, Matt Burke, Matt Robenolt, Matteo Ruina, Maurizio Tomasi, Max, Max Schulze, MaximAL, Maxime Thirouin, MichaIng, Michael Jephcote, Michael Rienstra, Michael Tilli, Mike Boone, MikeLund, MikolajTwarog, Mingxuan Lin, Naveen, Nicholas Rishel, Nico Stapelbroek, Nicolas Braud-Santoni, Nicolas Perraut, Niels Peter Roest, Nils Jakobi, NinoM4ster, Nitroretro, NoLooseEnds, Oliver Freyermuth, Otiel, Oyebanji Jacob Mayowa, Pablo, Pascal Jungblut, Paul Brit, Pawel Palenica, Paweł Rozlach, Peter Badida, Peter Dave Hello, Peter Hoeg, Peter Marquardt, Phani Rithvij, Phil Davis, Phill Luby, Pier Paolo Ramon, Piotr Bejda, Pramodh KP, Quentin Hibon, Rahmi Pruitt, Richard Hartmann, Robert Carosi, Roberto Santalla, Robin Schoonover, Roman Zaynetdinov, Ross Smith II, Ruslan Yevdokymov, Ryan Qian, Sacheendra Talluri, Scott Klupfel, Shaarad Dalvi, Simon Mwepu, Sly_tom_cat, Stefan Kuntz, Steven Eckhoff, Suhas Gundimeda, Taylor Khan, Thomas Hipp, Tim Abell, Tim Howes, Tobias Klauser, Tobias Nygren, Tobias Tom, Tom Jakubowski, Tommy Thorn, Tully Robinson, Tyler Brazier, Tyler Kropp, Unrud, Veeti Paananen, Victor Buinsky, Vil Brekin, Vladimir Rusinov, William A. Kennington III, Xavier O., Yannic A., andresvia, andyleap, boomsquared, bt90, chenrui, chucic, derekriemer, desbma, georgespatton, ghjklw, ignacy123, janost, jaseg, jelle van der Waa, jtagcat, klemens, marco-m, mclang, mv1005, otbutz, overkill, perewa, red_led, rubenbe, sec65, villekalliomaki, wangguoliang, wouter bolsterlee, xarx00, xjtdy888, 佛跳墙, 落心
-    </div>
-    </div>
-    <hr />
+          </div>
+        </div>
+      </div>
 
-    <p translate>Syncthing includes the following software or portions thereof:</p>
-    <ul class="list-unstyled two-columns" id="copyright-notices">
-      <li><a href="http://getbootstrap.com/">Bootstrap</a>, Copyright &copy; 2011-2016 Twitter, Inc.</li>
-      <li><a href="https://angularjs.org/">AngularJS</a>, Copyright &copy; 2010-2014, 2016 Google, Inc.</li>
-      <li><a href="http://www.daterangepicker.com/">Date Range Picker</a>, Copyright &copy; 2012-2018 Dan Grossman.</li>
-      <li><a href="https://github.com/mar10/fancytree">JQuery Fancytree Plugin</a>, Copyright &copy; 2008-2018 Martin Wendt.</li>
-      <li><a href="https://forkaweso.me/Fork-Awesome/">Fork Awesome</a>, Copyright &copy; 2018 Dave Gandy &amp; Fork Awesome.</li>
-      <li><a href="http://jquery.com/">jQuery JavaScript Library</a>, Copyright &copy; jQuery Foundation and other contributors.</li>
-      <li><a href="http://momentjs.com/">moment.js</a>, Copyright &copy; JS Foundation and other contributors.</li>
-      <li><a href="https://prometheus.io/">Prometheus</a>, Copyright &copy; 2012-2015 The Prometheus Authors.</li>
-      <li><a href="https://github.com/AudriusButkevicius/go-nat-pmp">AudriusButkevicius/go-nat-pmp</a>, Copyright &copy; 2013 John Howard Palevich.</li>
-      <li><a href="https://github.com/AudriusButkevicius/recli">AudriusButkevicius/recli</a>, Copyright &copy; 2019 Audrius Butkevicius.</li>
-      <li><a href="https://github.com/beorn7/perks">beorn7/perks</a>, Copyright &copy; 2013 Blake Mizerany.</li>
-      <li><a href="https://github.com/pierrec/lz4">pierrec/lz4</a>, Copyright &copy; 2015 Pierre Curto.</li>
-      <li><a href="https://github.com/calmh/du">calmh/du</a>, Public domain.</li>
-      <li><a href="https://github.com/calmh/xdr">calmh/xdr</a>, Copyright &copy; 2014 Jakob Borg.</li>
-      <li><a href="https://github.com/chmduquesne/rollinghash">chmduquesne/rollinghash</a>, Copyright &copy; 2015 Christophe-Marie Duquesne.</li>
-      <li><a href="https://github.com/d4l3k/messagediff">d4l3k/messagediff</a>, Copyright &copy; 2015 Tristan Rice.</li>
-      <li><a href="https://github.com/flynn-archive/go-shlex">flynn-archive/go-shlex</a>, Copyright &copy; 2012 Google Inc.</li>
-      <li><a href="https://github.com/gobwas/glob">gobwas/glob</a>, Copyright &copy; 2016 Sergey Kamardin.</li>
-      <li><a href="https://github.com/gogo/protobuf">gogo/protobuf</a>, Copyright &copy; 2013 The GoGo Authors.</li>
-      <li><a href="https://github.com/golang/groupcache">golang/groupcache</a>, Copyright &copy; 2013 Google Inc.</li>
-      <li><a href="https://github.com/golang/protobuf">golang/protobuf</a>, Copyright &copy; 2010 The Go Authors.</li>
-      <li><a href="https://github.com/golang/snappy">golang/snappy</a>, Copyright &copy; 2011 The Snappy-Go Authors.</li>
-      <li><a href="https://github.com/jackpal/gateway">jackpal/gateway</a>, Copyright &copy; 2010 Jack Palevich.</li>
-      <li><a href="https://github.com/kballard/go-shellquote">kballard/go-shellquote</a>, Copyright &copy; 2014 Kevin Ballard.</li>
-      <li><a href="https://github.com/lib/pq">lib/pq</a>, Copyright &copy; 2011-2013, 'pq' Contributors, portions Copyright &copy; 2011 Blake Mizerany.</li>
-      <li><a href="https://github.com/mattn/go-isatty">mattn/go-isatty</a>, Copyright &copy; Yasuhiro MATSUMOTO.</li>
-      <li><a href="https://github.com/matttproud/golang_protobuf_extensions">matttproud/golang_protobuf_extensions</a>, Copyright &copy; 2012 Matt T. Proud.</li>
-      <li><a href="https://github.com/minio/sha256-simd">minio/sha256-simd</a>, Copyright &copy; 2016-2017 Minio, Inc.</li>
-      <li><a href="https://github.com/oschwald/geoip2-golang">oschwald/geoip2-golang</a>, Copyright &copy; 2015, Gregory J. Oschwald.</li>
-      <li><a href="https://github.com/oschwald/maxminddb-golang">oschwald/maxminddb-golang</a>, Copyright &copy; 2015, Gregory J. Oschwald.</li>
-      <li><a href="https://github.com/petermattis/goid">petermattis/goid</a>, Copyright &copy; 2015-2016 Peter Mattis.</li>
-      <li><a href="https://github.com/pkg/errors">pkg/errors</a>, Copyright &copy; 2015, Dave Cheney.</li>
-      <li><a href="https://github.com/rcrowley/go-metrics">rcrowley/go-metrics</a>, Copyright &copy; 2012 Richard Crowley.</li>
-      <li><a href="https://github.com/sasha-s/go-deadlock">sasha-s/go-deadlock</a>, Copyright &copy; 2016 sasha-s.</li>
-      <li><a href="https://github.com/syncthing/notify">syncthing/notify</a>, Copyright &copy; 2014-2015 The Notify Authors.</li>
-      <li><a href="https://github.com/syndtr/goleveldb">syndtr/goleveldb</a>, Copyright &copy; 2012 Suryandaru Triandana.</li>
-      <li><a href="https://github.com/thejerf/suture">thejerf/suture</a>, Copyright &copy; 2014-2015 Barracuda Networks, Inc.</li>
-      <li><a href="https://github.com/urfave/cli">urfave/cli</a>, Copyright &copy; 2016 Jeremy Saenz &amp; Contributors.</li>
-      <li><a href="https://github.com/vitrun/qart">vitrun/qart</a>, Copyright &copy; 2010-2011 The Go Authors.</li>
-      <li><a href="https://gopkg.in/asn1-ber.v1">gopkg.in/asn1-ber.v1</a>, Copyright &copy; 2011-2015 Michael Mitton, portions Copyright &copy; 2015-2016 go-asn1-ber Authors.</li>
-      <li><a href="https://gopkg.in/ldap.v2">gopkg.in/ldap.v2</a>, Copyright &copy; 2011-2015 Michael Mitton, portions Copyright &copy; 2015-2016 go-ldap Authors.</li>
-      <li><a href="https://golang.org">The Go Programming Language</a>, Copyright &copy; 2009 The Go Authors.</li>
-      <li>Font Awesome by Dave Gandy - <a href="http://fontawesome.io/">http://fontawesome.io</a></li>
-    </ul>
+      <div id="about-includes" class="tab-pane">
+        <p translate>Syncthing includes the following software or portions thereof:</p>
+        <ul class="list-unstyled two-columns" id="copyright-notices">
+          <li><a href="http://getbootstrap.com/">Bootstrap</a>, Copyright &copy; 2011-2016 Twitter, Inc.</li>
+          <li><a href="https://angularjs.org/">AngularJS</a>, Copyright &copy; 2010-2014, 2016 Google, Inc.</li>
+          <li><a href="http://www.daterangepicker.com/">Date Range Picker</a>, Copyright &copy; 2012-2018 Dan Grossman.</li>
+          <li><a href="https://github.com/mar10/fancytree">JQuery Fancytree Plugin</a>, Copyright &copy; 2008-2018 Martin Wendt.</li>
+          <li><a href="https://forkaweso.me/Fork-Awesome/">Fork Awesome</a>, Copyright &copy; 2018 Dave Gandy &amp; Fork Awesome.</li>
+          <li><a href="http://jquery.com/">jQuery JavaScript Library</a>, Copyright &copy; jQuery Foundation and other contributors.</li>
+          <li><a href="http://momentjs.com/">moment.js</a>, Copyright &copy; JS Foundation and other contributors.</li>
+          <li><a href="https://prometheus.io/">Prometheus</a>, Copyright &copy; 2012-2015 The Prometheus Authors.</li>
+          <li><a href="https://github.com/AudriusButkevicius/go-nat-pmp">AudriusButkevicius/go-nat-pmp</a>, Copyright &copy; 2013 John Howard Palevich.</li>
+          <li><a href="https://github.com/AudriusButkevicius/recli">AudriusButkevicius/recli</a>, Copyright &copy; 2019 Audrius Butkevicius.</li>
+          <li><a href="https://github.com/beorn7/perks">beorn7/perks</a>, Copyright &copy; 2013 Blake Mizerany.</li>
+          <li><a href="https://github.com/pierrec/lz4">pierrec/lz4</a>, Copyright &copy; 2015 Pierre Curto.</li>
+          <li><a href="https://github.com/calmh/du">calmh/du</a>, Public domain.</li>
+          <li><a href="https://github.com/calmh/xdr">calmh/xdr</a>, Copyright &copy; 2014 Jakob Borg.</li>
+          <li><a href="https://github.com/chmduquesne/rollinghash">chmduquesne/rollinghash</a>, Copyright &copy; 2015 Christophe-Marie Duquesne.</li>
+          <li><a href="https://github.com/d4l3k/messagediff">d4l3k/messagediff</a>, Copyright &copy; 2015 Tristan Rice.</li>
+          <li><a href="https://github.com/flynn-archive/go-shlex">flynn-archive/go-shlex</a>, Copyright &copy; 2012 Google Inc.</li>
+          <li><a href="https://github.com/gobwas/glob">gobwas/glob</a>, Copyright &copy; 2016 Sergey Kamardin.</li>
+          <li><a href="https://github.com/gogo/protobuf">gogo/protobuf</a>, Copyright &copy; 2013 The GoGo Authors.</li>
+          <li><a href="https://github.com/golang/groupcache">golang/groupcache</a>, Copyright &copy; 2013 Google Inc.</li>
+          <li><a href="https://github.com/golang/protobuf">golang/protobuf</a>, Copyright &copy; 2010 The Go Authors.</li>
+          <li><a href="https://github.com/golang/snappy">golang/snappy</a>, Copyright &copy; 2011 The Snappy-Go Authors.</li>
+          <li><a href="https://github.com/jackpal/gateway">jackpal/gateway</a>, Copyright &copy; 2010 Jack Palevich.</li>
+          <li><a href="https://github.com/kballard/go-shellquote">kballard/go-shellquote</a>, Copyright &copy; 2014 Kevin Ballard.</li>
+          <li><a href="https://github.com/lib/pq">lib/pq</a>, Copyright &copy; 2011-2013, 'pq' Contributors, portions Copyright &copy; 2011 Blake Mizerany.</li>
+          <li><a href="https://github.com/mattn/go-isatty">mattn/go-isatty</a>, Copyright &copy; Yasuhiro MATSUMOTO.</li>
+          <li><a href="https://github.com/matttproud/golang_protobuf_extensions">matttproud/golang_protobuf_extensions</a>, Copyright &copy; 2012 Matt T. Proud.</li>
+          <li><a href="https://github.com/minio/sha256-simd">minio/sha256-simd</a>, Copyright &copy; 2016-2017 Minio, Inc.</li>
+          <li><a href="https://github.com/oschwald/geoip2-golang">oschwald/geoip2-golang</a>, Copyright &copy; 2015, Gregory J. Oschwald.</li>
+          <li><a href="https://github.com/oschwald/maxminddb-golang">oschwald/maxminddb-golang</a>, Copyright &copy; 2015, Gregory J. Oschwald.</li>
+          <li><a href="https://github.com/petermattis/goid">petermattis/goid</a>, Copyright &copy; 2015-2016 Peter Mattis.</li>
+          <li><a href="https://github.com/pkg/errors">pkg/errors</a>, Copyright &copy; 2015, Dave Cheney.</li>
+          <li><a href="https://github.com/rcrowley/go-metrics">rcrowley/go-metrics</a>, Copyright &copy; 2012 Richard Crowley.</li>
+          <li><a href="https://github.com/sasha-s/go-deadlock">sasha-s/go-deadlock</a>, Copyright &copy; 2016 sasha-s.</li>
+          <li><a href="https://github.com/syncthing/notify">syncthing/notify</a>, Copyright &copy; 2014-2015 The Notify Authors.</li>
+          <li><a href="https://github.com/syndtr/goleveldb">syndtr/goleveldb</a>, Copyright &copy; 2012 Suryandaru Triandana.</li>
+          <li><a href="https://github.com/thejerf/suture">thejerf/suture</a>, Copyright &copy; 2014-2015 Barracuda Networks, Inc.</li>
+          <li><a href="https://github.com/urfave/cli">urfave/cli</a>, Copyright &copy; 2016 Jeremy Saenz &amp; Contributors.</li>
+          <li><a href="https://github.com/vitrun/qart">vitrun/qart</a>, Copyright &copy; 2010-2011 The Go Authors.</li>
+          <li><a href="https://gopkg.in/asn1-ber.v1">gopkg.in/asn1-ber.v1</a>, Copyright &copy; 2011-2015 Michael Mitton, portions Copyright &copy; 2015-2016 go-asn1-ber Authors.</li>
+          <li><a href="https://gopkg.in/ldap.v2">gopkg.in/ldap.v2</a>, Copyright &copy; 2011-2015 Michael Mitton, portions Copyright &copy; 2015-2016 go-ldap Authors.</li>
+          <li><a href="https://golang.org">The Go Programming Language</a>, Copyright &copy; 2009 The Go Authors.</li>
+          <li>Font Awesome by Dave Gandy - <a href="http://fontawesome.io/">http://fontawesome.io</a></li>
+        </ul>
+      </div>
+
+      <div id="about-paths" class="tab-pane">
+        <table class="table table-striped table-auto">
+          <caption><label translate>Internally used paths:</label></caption>
+          <tbody>
+            <tr>
+              <th translate>User Home</th>
+              <td><code class="file-path">{{ about.paths['baseDir-userHome'] }}</code></td>
+            </tr>
+            <tr>
+              <th><strong translate>Configuration Directory</strong></th>
+              <td><code class="file-path"><strong>{{ about.paths['baseDir-config'] }}</strong></code></td>
+            </tr>
+            <tr>
+              <th translate>Configuration File</th>
+              <td><code class="file-path">{{ about.paths['config'] }}</code></td>
+            </tr>
+            <tr>
+              <th translate>Device Certificate</th>
+              <td><code class="file-path">{{ about.paths['certFile'] }}</code>
+                <br /><code class="file-path">{{ about.paths['keyFile'] }}</code></td>
+            </tr>
+            <tr>
+              <th translate>GUI / API HTTPS Certificate</th>
+              <td><code class="file-path">{{ about.paths['httpsCertFile'] }}</code>
+                <br /><code class="file-path">{{ about.paths['httpsKeyFile'] }}</code></td>
+            </tr>
+            <tr>
+              <th translate>Database Location</th>
+              <td><code class="file-path">{{ about.paths['database'] }}</code></td>
+            </tr>
+            <tr>
+              <th translate>Log File</th>
+              <td><code class="file-path">{{ about.paths['logFile'] }}</code></td>
+            </tr>
+            <tr>
+              <th translate>GUI Override Directory</th>
+              <td><code class="file-path">{{ about.paths['guiAssets'] }}</code></td>
+            </tr>
+          </tbody>
+        </table>
+      </div>
+
+    </div>
   </div>
   <div class="modal-footer">
     <button type="button" class="btn btn-default btn-sm" data-dismiss="modal">
diff --git a/gui/default/syncthing/core/syncthingController.js b/gui/default/syncthing/core/syncthingController.js
index 60655fcc6d3..c9e773ff4a3 100755
--- a/gui/default/syncthing/core/syncthingController.js
+++ b/gui/default/syncthing/core/syncthingController.js
@@ -1399,6 +1399,19 @@ angular.module('syncthing.core')
             }
         };
 
+        $scope.about = {
+            paths: {},
+            refreshPaths: function () {
+                $http.get(urlbase + '/system/paths').success(function (data) {
+                    $scope.about.paths = data;
+                }).error($scope.emitHTTPError);
+            },
+            show: function () {
+                $scope.about.refreshPaths();
+                $('#about').modal("show");
+            },
+        };
+
         $scope.discardChangedSettings = function () {
             $("#discard-changes-confirmation").modal("hide");
             $("#settings").off("hide.bs.modal").modal("hide");
diff --git a/lib/api/api.go b/lib/api/api.go
index b6ba94175cd..fe7ae36effa 100644
--- a/lib/api/api.go
+++ b/lib/api/api.go
@@ -268,6 +268,7 @@ func (s *service) Serve(ctx context.Context) error {
 	restMux.HandlerFunc(http.MethodGet, "/rest/system/connections", s.getSystemConnections)   // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/system/discovery", s.getSystemDiscovery)       // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/system/error", s.getSystemError)               // -
+	restMux.HandlerFunc(http.MethodGet, "/rest/system/paths", s.getSystemPaths)               // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/system/ping", s.restPing)                      // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/system/status", s.getSystemStatus)             // -
 	restMux.HandlerFunc(http.MethodGet, "/rest/system/upgrade", s.getSystemUpgrade)           // -
@@ -693,6 +694,10 @@ func (*service) restPing(w http.ResponseWriter, _ *http.Request) {
 	sendJSON(w, map[string]string{"ping": "pong"})
 }
 
+func (*service) getSystemPaths(w http.ResponseWriter, _ *http.Request) {
+	sendJSON(w, locations.ListExpandedPaths())
+}
+
 func (s *service) getJSMetadata(w http.ResponseWriter, _ *http.Request) {
 	meta, _ := json.Marshal(map[string]string{
 		"deviceID": s.id.String(),
diff --git a/lib/locations/locations.go b/lib/locations/locations.go
index a249866d759..0fa488e7e23 100644
--- a/lib/locations/locations.go
+++ b/lib/locations/locations.go
@@ -33,9 +33,8 @@ const (
 	CsrfTokens    LocationEnum = "csrfTokens"
 	PanicLog      LocationEnum = "panicLog"
 	AuditLog      LocationEnum = "auditLog"
-	GUIAssets     LocationEnum = "GUIAssets"
+	GUIAssets     LocationEnum = "guiAssets"
 	DefFolder     LocationEnum = "defFolder"
-	FailuresFile  LocationEnum = "FailuresFile"
 )
 
 type BaseDirEnum string
@@ -67,6 +66,24 @@ func init() {
 	}
 }
 
+// Set overrides a location to the given path, making sure to it points to an
+// absolute path first.  Only the special "-" value will be used verbatim.
+func Set(locationName LocationEnum, path string) error {
+	if !filepath.IsAbs(path) && path != "-" {
+		var err error
+		path, err = filepath.Abs(path)
+		if err != nil {
+			return err
+		}
+	}
+	_, ok := locationTemplates[locationName]
+	if !ok {
+		return fmt.Errorf("unknown location: %s", locationName)
+	}
+	locations[locationName] = filepath.Clean(path)
+	return nil
+}
+
 func SetBaseDir(baseDirName BaseDirEnum, path string) error {
 	if !filepath.IsAbs(path) {
 		var err error
@@ -105,7 +122,6 @@ var locationTemplates = map[LocationEnum]string{
 	AuditLog:      "${data}/audit-${timestamp}.log",
 	GUIAssets:     "${config}/gui",
 	DefFolder:     "${userHome}/Sync",
-	FailuresFile:  "${data}/failures-unreported.txt",
 }
 
 var locations = make(map[LocationEnum]string)
@@ -129,6 +145,32 @@ func expandLocations() error {
 	return nil
 }
 
+// ListExpandedPaths returns a machine-readable mapping of the currently configured locations.
+func ListExpandedPaths() map[string]string {
+	res := make(map[string]string, len(locations))
+	for key, path := range baseDirs {
+		res["baseDir-"+string(key)] = path
+	}
+	for key, path := range locations {
+		res[string(key)] = path
+	}
+	return res
+}
+
+// PrettyPaths returns a nicely formatted, human-readable listing
+func PrettyPaths() string {
+	var b strings.Builder
+	fmt.Fprintf(&b, "Configuration file:\n\t%s\n\n", Get(ConfigFile))
+	fmt.Fprintf(&b, "Device private key & certificate files:\n\t%s\n\t%s\n\n", Get(KeyFile), Get(CertFile))
+	fmt.Fprintf(&b, "GUI / API HTTPS private key & certificate files:\n\t%s\n\t%s\n\n", Get(HTTPSKeyFile), Get(HTTPSCertFile))
+	fmt.Fprintf(&b, "Database location:\n\t%s\n\n", Get(Database))
+	fmt.Fprintf(&b, "Log file:\n\t%s\n\n", Get(LogFile))
+	fmt.Fprintf(&b, "GUI override directory:\n\t%s\n\n", Get(GUIAssets))
+	fmt.Fprintf(&b, "CSRF tokens file:\n\t%s\n\n", Get(CsrfTokens))
+	fmt.Fprintf(&b, "Default sync folder directory:\n\t%s\n\n", Get(DefFolder))
+	return b.String()
+}
+
 // defaultConfigDir returns the default configuration directory, as figured
 // out by various the environment variables present on each platform, or dies
 // trying.
diff --git a/lib/syncthing/syncthing.go b/lib/syncthing/syncthing.go
index b0fe9adbd2f..f82df4c928c 100644
--- a/lib/syncthing/syncthing.go
+++ b/lib/syncthing/syncthing.go
@@ -54,7 +54,6 @@ const (
 )
 
 type Options struct {
-	AssetDir         string
 	AuditWriter      io.Writer
 	DeadlockTimeoutS int
 	NoUpgrade        bool
@@ -423,7 +422,7 @@ func (a *App) setupGUI(m model.Model, defaultSub, diskSub events.BufferedSubscri
 	summaryService := model.NewFolderSummaryService(a.cfg, m, a.myID, a.evLogger)
 	a.mainService.Add(summaryService)
 
-	apiSvc := api.New(a.myID, a.cfg, a.opts.AssetDir, tlsDefaultCommonName, m, defaultSub, diskSub, a.evLogger, discoverer, connectionsService, urService, summaryService, errors, systemLog, a.opts.NoUpgrade)
+	apiSvc := api.New(a.myID, a.cfg, locations.Get(locations.GUIAssets), tlsDefaultCommonName, m, defaultSub, diskSub, a.evLogger, discoverer, connectionsService, urService, summaryService, errors, systemLog, a.opts.NoUpgrade)
 	a.mainService.Add(apiSvc)
 
 	if err := apiSvc.WaitForStart(); err != nil {
diff --git a/script/authors.go b/script/authors.go
index 55a77ebae93..f63d1924ca4 100644
--- a/script/authors.go
+++ b/script/authors.go
@@ -117,7 +117,7 @@ func main() {
 
 	authorsRe := regexp.MustCompile(`(?s)id="contributor-list">.*?</div>`)
 	bs := readAll(htmlFile)
-	bs = authorsRe.ReplaceAll(bs, []byte("id=\"contributor-list\">\n"+replacement+"\n    </div>"))
+	bs = authorsRe.ReplaceAll(bs, []byte("id=\"contributor-list\">\n"+replacement+"\n          </div>"))
 
 	if err := os.WriteFile(htmlFile, bs, 0644); err != nil {
 		log.Fatal(err)


Test patch: