@@ -94,10 +94,12 @@ func StaticFileHandler(file string, filesystem fs.FS) HandlerFunc {
9494 }
9595}
9696
97- // defaultFS emulates os.Open behaviour with filesystem opened by `os.DirFs`. Difference between `os.Open` and `fs.Open`
98- // is that FS does not allow to open path that start with `..` or `/` etc. For example previously you could have `../images`
99- // in your application but `fs := os.DirFS("./")` would not allow you to use `fs.Open("../images")` and this would break
100- // all old applications that rely on being able to traverse up from current executable run path.
97+ // defaultFS exists to preserve pre v4.7.0 behaviour where files were open by `os.Open`.
98+ // v4.7 introduced `echo.Filesystem` field which is Go1.16+ `fs.Fs` interface.
99+ // Difference between `os.Open` and `fs.Open` is that FS does not allow opening path that start with `.`, `..` or `/`
100+ // etc. For example previously you could have `../images` in your application but `fs := os.DirFS("./")` would not
101+ // allow you to use `fs.Open("../images")` and this would break all old applications that rely on being able to
102+ // traverse up from current executable run path.
101103// NB: private because you really should use fs.FS implementation instances
102104type defaultFS struct {
103105 prefix string
@@ -108,20 +110,26 @@ func newDefaultFS() *defaultFS {
108110 dir , _ := os .Getwd ()
109111 return & defaultFS {
110112 prefix : dir ,
111- fs : os . DirFS ( dir ) ,
113+ fs : nil ,
112114 }
113115}
114116
115117func (fs defaultFS ) Open (name string ) (fs.File , error ) {
116- return fs .fs .Open (filepath .ToSlash (filepath .Clean (name )))
118+ if fs .fs == nil {
119+ return os .Open (name )
120+ }
121+ return fs .fs .Open (name )
117122}
118123
119124func subFS (currentFs fs.FS , root string ) (fs.FS , error ) {
120125 root = filepath .ToSlash (filepath .Clean (root )) // note: fs.FS operates only with slashes. `ToSlash` is necessary for Windows
121126 if dFS , ok := currentFs .(* defaultFS ); ok {
122- // we need to make exception for `defaultFS` instances as it interprets root prefix differently from fs.FS to
123- // allow cases when root is given as `../somepath` which is not valid for fs.FS
124- root = filepath .Join (dFS .prefix , root )
127+ // we need to make exception for `defaultFS` instances as it interprets root prefix differently from fs.FS.
128+ // fs.Fs.Open does not like relative paths ("./", "../") and absolute paths at all but prior echo.Filesystem we
129+ // were able to use paths like `./myfile.log`, `/etc/hosts` and these would work fine with `os.Open` but not with fs.Fs
130+ if len (root ) > 0 && root [0 ] != '/' {
131+ root = filepath .Join (dFS .prefix , root )
132+ }
125133 return & defaultFS {
126134 prefix : root ,
127135 fs : os .DirFS (root ),
0 commit comments