aboutsummaryrefslogtreecommitdiff
path: root/libgo/go/net/http/fs.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/net/http/fs.go')
-rw-r--r--libgo/go/net/http/fs.go76
1 files changed, 58 insertions, 18 deletions
diff --git a/libgo/go/net/http/fs.go b/libgo/go/net/http/fs.go
index 75720234c25..f61c138c1d9 100644
--- a/libgo/go/net/http/fs.go
+++ b/libgo/go/net/http/fs.go
@@ -17,6 +17,7 @@ import (
"os"
"path"
"path/filepath"
+ "sort"
"strconv"
"strings"
"time"
@@ -62,30 +63,34 @@ type FileSystem interface {
type File interface {
io.Closer
io.Reader
+ io.Seeker
Readdir(count int) ([]os.FileInfo, error)
- Seek(offset int64, whence int) (int64, error)
Stat() (os.FileInfo, error)
}
func dirList(w ResponseWriter, f File) {
+ dirs, err := f.Readdir(-1)
+ if err != nil {
+ // TODO: log err.Error() to the Server.ErrorLog, once it's possible
+ // for a handler to get at its Server via the ResponseWriter. See
+ // Issue 12438.
+ Error(w, "Error reading directory", StatusInternalServerError)
+ return
+ }
+ sort.Sort(byName(dirs))
+
w.Header().Set("Content-Type", "text/html; charset=utf-8")
fmt.Fprintf(w, "<pre>\n")
- for {
- dirs, err := f.Readdir(100)
- if err != nil || len(dirs) == 0 {
- break
- }
- for _, d := range dirs {
- name := d.Name()
- if d.IsDir() {
- name += "/"
- }
- // name may contain '?' or '#', which must be escaped to remain
- // part of the URL path, and not indicate the start of a query
- // string or fragment.
- url := url.URL{Path: name}
- fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
+ for _, d := range dirs {
+ name := d.Name()
+ if d.IsDir() {
+ name += "/"
}
+ // name may contain '?' or '#', which must be escaped to remain
+ // part of the URL path, and not indicate the start of a query
+ // string or fragment.
+ url := url.URL{Path: name}
+ fmt.Fprintf(w, "<a href=\"%s\">%s</a>\n", url.String(), htmlReplacer.Replace(name))
}
fmt.Fprintf(w, "</pre>\n")
}
@@ -364,8 +369,8 @@ func serveFile(w ResponseWriter, r *Request, fs FileSystem, name string, redirec
}
defer f.Close()
- d, err1 := f.Stat()
- if err1 != nil {
+ d, err := f.Stat()
+ if err != nil {
msg, code := toHTTPError(err)
Error(w, msg, code)
return
@@ -446,15 +451,44 @@ func localRedirect(w ResponseWriter, r *Request, newPath string) {
// ServeFile replies to the request with the contents of the named
// file or directory.
//
+// If the provided file or direcory name is a relative path, it is
+// interpreted relative to the current directory and may ascend to parent
+// directories. If the provided name is constructed from user input, it
+// should be sanitized before calling ServeFile. As a precaution, ServeFile
+// will reject requests where r.URL.Path contains a ".." path element.
+//
// As a special case, ServeFile redirects any request where r.URL.Path
// ends in "/index.html" to the same path, without the final
// "index.html". To avoid such redirects either modify the path or
// use ServeContent.
func ServeFile(w ResponseWriter, r *Request, name string) {
+ if containsDotDot(r.URL.Path) {
+ // Too many programs use r.URL.Path to construct the argument to
+ // serveFile. Reject the request under the assumption that happened
+ // here and ".." may not be wanted.
+ // Note that name might not contain "..", for example if code (still
+ // incorrectly) used filepath.Join(myDir, r.URL.Path).
+ Error(w, "invalid URL path", StatusBadRequest)
+ return
+ }
dir, file := filepath.Split(name)
serveFile(w, r, Dir(dir), file, false)
}
+func containsDotDot(v string) bool {
+ if !strings.Contains(v, "..") {
+ return false
+ }
+ for _, ent := range strings.FieldsFunc(v, isSlashRune) {
+ if ent == ".." {
+ return true
+ }
+ }
+ return false
+}
+
+func isSlashRune(r rune) bool { return r == '/' || r == '\\' }
+
type fileHandler struct {
root FileSystem
}
@@ -585,3 +619,9 @@ func sumRangesSize(ranges []httpRange) (size int64) {
}
return
}
+
+type byName []os.FileInfo
+
+func (s byName) Len() int { return len(s) }
+func (s byName) Less(i, j int) bool { return s[i].Name() < s[j].Name() }
+func (s byName) Swap(i, j int) { s[i], s[j] = s[j], s[i] }