Skip to content

Commit 3d54706

Browse files
committed
feat(serverHandler): prevent mutate items under directory if aliased
1 parent 0335a60 commit 3d54706

File tree

8 files changed

+105
-39
lines changed

8 files changed

+105
-39
lines changed

README.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -120,12 +120,12 @@ server [options]
120120
Specify default index file for directory.
121121
122122
-a|--alias <separator><url-path><separator><fs-path> ...
123-
Set path alias. e.g. ":/doc:/usr/share/doc"
123+
Set path alias.
124+
Mount a file system path to URL path.
125+
e.g. ":/doc:/usr/share/doc"
124126
125127
-U|--global-upload
126128
Allow upload files for all url paths.
127-
If filename exists, will try to remove it first if deleting is enabled(e.g. by --delete),
128-
otherwise will try to add or increase numeric suffix.
129129
Use it with care.
130130
-u|--upload <url-path> ...
131131
Set url paths(and sub paths) that allows to upload files.
@@ -134,20 +134,33 @@ server [options]
134134
Similar to --upload, but use file system path instead of url path.
135135
Use it with care.
136136
137+
Notes for upload options:
138+
If filename exists and is a regular file,
139+
will try to remove it first if deleting is enabled(e.g. by --delete),
140+
otherwise will try to add or increase numeric suffix.
141+
137142
--global-mkdir
138143
Allow create sub directory under all url paths.
139144
--mkdir <url-path> ...
140145
Allow create sub directory under specific url paths(and sub paths).
141146
--mkdir-dir <fs-path> ...
142147
Similar to --mkdir, but use file system path instead of url path.
143148
149+
Notes for mkdir options:
150+
To avoid ambiguity, names shadowed by aliased items cannot be created.
151+
144152
--global-delete
145153
Allow delete items under all url paths.
146154
--delete <url-path> ...
147155
Allow delete items under specific url paths(and sub paths).
148156
--delete-dir <fs-path> ...
149157
Similar to --delete, but use file system path instead of url path.
150158
159+
Notes for delete options:
160+
To avoid ambiguity, aliased items under a path cannot be deleted.
161+
Non-aliased files/directories inside aliased items still can be deleted.
162+
To avoid ambiguity, files/directories shadowed by aliased items cannot be deleted.
163+
151164
-A|--global-archive
152165
Allow user to download the whole contents of current directory for all url paths.
153166
A download link will appear on top part of the page.

README.zh-CN.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -118,33 +118,46 @@ server [选项]
118118
指定目录默认页面文件。
119119
120120
-a|--alias <分隔符><URL路径><分隔符><文件系统路径> ...
121-
设置路径别名。例如:“:/doc:/usr/share/doc”。
121+
设置路径别名。
122+
将某个文件系统路径挂载到URL路径下。
123+
例如:“:/doc:/usr/share/doc”。
122124
123125
-U|--global-upload
124126
对所有URL路径开启上传权限。
125-
如果文件已存在,若已启用删除(例如--delete选项),则尝试先删除文件,
126-
否则尝试添加或递增数字后缀。
127127
请谨慎使用。
128128
-u|--upload <URL路径> ...
129129
设置允许上传的URL路径(及子路径)。
130130
请谨慎使用。
131131
--upload-dir <文件系统路径> ...
132132
与--upload类似,但指定的是文件系统路径,而不是URL路径。
133133
134+
上传选项注意事项:
135+
如果名称已存在且是常规文件,
136+
若已启用删除(例如--delete选项),则尝试先删除文件,
137+
否则尝试添加或递增数字后缀。
138+
134139
--global-mkdir
135140
对所有URL路径开启创建子目录权限。
136141
--mkdir <url-path> ...
137142
设置允许创建子目录的URL路径(及子路径)。
138143
--mkdir-dir <fs-path> ...
139144
与--mkdir类似,但指定的是文件系统路径,而不是URL路径。
140145
146+
创建子目录选项注意事项:
147+
为避免歧义,被别名遮蔽的目录名不能被创建。
148+
141149
--global-delete
142150
对所有URL路径开启删除子项权限。
143151
--delete <url-path> ...
144152
设置允许删除子项的URL路径(及子路径)。
145153
--delete-dir <fs-path> ...
146154
与--delete类似,但指定的是文件系统路径,而不是URL路径。
147155
156+
删除选项注意事项:
157+
为避免歧义,URL路径下挂载的别名不能被删除。
158+
别名下的非别名文件/目录仍然可以被删除。
159+
为避免歧义,被别名遮蔽的正常文件/目录不能被删除。
160+
148161
-A|--global-archive
149162
对所有URL路径开启打包下载当前目录内容的功能。
150163
页面顶部会出现下载链接。

src/serverHandler/delete.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import (
44
"os"
55
)
66

7-
func (h *handler) deleteItems(fsPrefix string, files []string) {
7+
func (h *handler) deleteItems(fsPrefix string, files []string, aliasSubItems []os.FileInfo) {
88
errs := []error{}
99

1010
for _, filename := range files {
11+
if containsItem(aliasSubItems, filename) {
12+
continue
13+
}
1114
err := os.RemoveAll(fsPrefix + "/" + filename)
1215
if err != nil {
1316
errs = append(errs, err)

src/serverHandler/main.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,24 @@ func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
9090
}
9191

9292
switch {
93-
case data.CanUpload && r.URL.RawQuery == "upload" && r.Method == http.MethodPost:
94-
h.saveUploadFiles(h.root+data.handlerReqPath, data.CanDelete, r)
93+
case r.URL.RawQuery == "upload":
94+
if data.CanUpload && r.Method == http.MethodPost {
95+
h.saveUploadFiles(h.root+data.handlerReqPath, data.CanDelete, data.AliasSubItems, r)
96+
}
9597
http.Redirect(w, r, r.URL.Path, http.StatusFound)
9698
return
97-
case data.CanMkdir && strings.HasPrefix(r.URL.RawQuery, "mkdir"):
98-
h.errHandler.LogError(r.ParseForm())
99-
h.mkdirs(h.root+data.handlerReqPath, r.Form["name"])
99+
case strings.HasPrefix(r.URL.RawQuery, "mkdir"):
100+
if data.CanMkdir {
101+
h.errHandler.LogError(r.ParseForm())
102+
h.mkdirs(h.root+data.handlerReqPath, r.Form["name"], data.AliasSubItems)
103+
}
100104
http.Redirect(w, r, r.URL.Path, http.StatusFound)
101105
return
102-
case data.CanDelete && strings.HasPrefix(r.URL.RawQuery, "delete"):
103-
h.errHandler.LogError(r.ParseForm())
104-
h.deleteItems(h.root+data.handlerReqPath, r.Form["name"])
106+
case strings.HasPrefix(r.URL.RawQuery, "delete"):
107+
if data.CanDelete {
108+
h.errHandler.LogError(r.ParseForm())
109+
h.deleteItems(h.root+data.handlerReqPath, r.Form["name"], data.AliasSubItems)
110+
}
105111
http.Redirect(w, r, r.URL.Path, http.StatusFound)
106112
return
107113
}

src/serverHandler/mkdir.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@ import (
44
"os"
55
)
66

7-
func (h *handler) mkdirs(fsPrefix string, files []string) {
7+
func (h *handler) mkdirs(fsPrefix string, files []string, aliasSubItems []os.FileInfo) {
88
errs := []error{}
99

1010
for _, filename := range files {
11+
if containsItem(aliasSubItems, filename) {
12+
continue
13+
}
1114
err := os.Mkdir(fsPrefix+"/"+filename, 0755)
1215
if err != nil {
1316
errs = append(errs, err)

src/serverHandler/responseData.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ type responseData struct {
3939
Item os.FileInfo
4040
ItemName string
4141
SubItems []os.FileInfo
42+
AliasSubItems []os.FileInfo
4243
SubItemsHtml []*itemHtml
4344
SubItemPrefix string
4445

@@ -112,11 +113,15 @@ func readdir(file *os.File, item os.FileInfo, visitFs bool) (subItems []os.FileI
112113
return file.Readdir(0)
113114
}
114115

115-
func (h *handler) mergeAlias(rawRequestPath string, item os.FileInfo, subItems []os.FileInfo) ([]os.FileInfo, []error) {
116-
errs := []error{}
116+
func (h *handler) mergeAlias(
117+
rawRequestPath string,
118+
item os.FileInfo,
119+
subItems []os.FileInfo,
120+
) (mergedSubItems, aliasSubItems []os.FileInfo, errs []error) {
121+
errs = []error{}
117122

118123
if (item != nil && !item.IsDir()) || len(h.aliases) == 0 {
119-
return subItems, errs
124+
return subItems, nil, errs
120125
}
121126

122127
for _, alias := range h.aliases {
@@ -167,6 +172,7 @@ func (h *handler) mergeAlias(rawRequestPath string, item os.FileInfo, subItems [
167172
} else {
168173
aliasSubItem = newFakeFileInfo(nextName, true)
169174
}
175+
aliasSubItems = append(aliasSubItems, aliasSubItem)
170176

171177
replaced := false
172178
for i, subItem := range subItems {
@@ -182,7 +188,7 @@ func (h *handler) mergeAlias(rawRequestPath string, item os.FileInfo, subItems [
182188
}
183189
}
184190

185-
return subItems, errs
191+
return subItems, aliasSubItems, errs
186192
}
187193

188194
func getSubItemPrefix(rawRequestPath string, tailSlash bool) string {
@@ -308,7 +314,7 @@ func (h *handler) getResponseData(r *http.Request) (data *responseData) {
308314
status = http.StatusInternalServerError
309315
}
310316

311-
subItems, _mergeErrs := h.mergeAlias(rawReqPath, item, subItems)
317+
subItems, aliasSubItems, _mergeErrs := h.mergeAlias(rawReqPath, item, subItems)
312318
if len(_mergeErrs) > 0 {
313319
errs = append(errs, _mergeErrs...)
314320
status = http.StatusInternalServerError
@@ -345,6 +351,7 @@ func (h *handler) getResponseData(r *http.Request) (data *responseData) {
345351
Item: item,
346352
ItemName: itemName,
347353
SubItems: subItems,
354+
AliasSubItems: aliasSubItems,
348355
SubItemsHtml: nil,
349356
SubItemPrefix: subItemPrefix,
350357

src/serverHandler/upload.go

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,17 @@ import (
1010
"strconv"
1111
)
1212

13-
func getAvailableFilename(fsPrefix, filename string) string {
13+
func getAvailableFilename(fsPrefix, filename string, mustAppendSuffix bool) string {
1414
if len(fsPrefix) == 0 {
1515
fsPrefix = "/"
1616
} else if fsPrefix[len(fsPrefix)-1] != '/' {
1717
fsPrefix = fsPrefix + "/"
1818
}
1919

20-
if _, err := os.Lstat(fsPrefix + filename); os.IsNotExist(err) {
21-
return filename
20+
if !mustAppendSuffix {
21+
if _, err := os.Lstat(fsPrefix + filename); os.IsNotExist(err) {
22+
return filename
23+
}
2224
}
2325

2426
filenamePrefix, filenameSuffix := util.SplitFilename(filename)
@@ -33,7 +35,7 @@ func getAvailableFilename(fsPrefix, filename string) string {
3335
return ""
3436
}
3537

36-
func (h *handler) saveUploadFiles(fsPrefix string, overwriteExists bool, r *http.Request) {
38+
func (h *handler) saveUploadFiles(fsPrefix string, overwriteExists bool, aliasSubItems []os.FileInfo, r *http.Request) {
3739
errs := []error{}
3840

3941
reader, err := r.MultipartReader()
@@ -55,22 +57,29 @@ func (h *handler) saveUploadFiles(fsPrefix string, overwriteExists bool, r *http
5557
if len(filename) == 0 {
5658
continue
5759
}
60+
61+
isFilenameAliased := containsItem(aliasSubItems, filename)
5862
var fsFilename string
59-
if overwriteExists {
60-
err := os.Remove(fsPrefix + "/" + filename)
61-
if err != nil && !os.IsNotExist(err) {
62-
errs = append(errs, err)
63-
// continue
63+
if overwriteExists && !isFilenameAliased {
64+
tryPath := fsPrefix + "/" + filename
65+
var info os.FileInfo
66+
info, err = os.Lstat(tryPath)
67+
if info != nil && !info.IsDir() {
68+
err = os.Remove(tryPath)
69+
if err != nil && !os.IsNotExist(err) {
70+
errs = append(errs, err)
71+
}
6472
// even remove failed, still try to write content to file by TRUNCATE mode
73+
fsFilename = filename
6574
}
66-
fsFilename = filename
67-
} else {
68-
fsFilename = getAvailableFilename(fsPrefix, filename)
69-
if len(fsFilename) == 0 {
70-
err := errors.New("no available filename for " + filename)
71-
errs = append(errs, err)
72-
continue
73-
}
75+
}
76+
if len(fsFilename) == 0 {
77+
fsFilename = getAvailableFilename(fsPrefix, filename, isFilenameAliased)
78+
}
79+
if len(fsFilename) == 0 {
80+
err := errors.New("no available filename for " + filename)
81+
errs = append(errs, err)
82+
continue
7483
}
7584

7685
fsPath := path.Clean(fsPrefix + "/" + fsFilename)

src/serverHandler/util.go

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,22 @@
11
package serverHandler
22

3-
import "net/http"
3+
import (
4+
"net/http"
5+
"os"
6+
)
47

58
func needResponseBody(method string) bool {
69
return method != http.MethodHead &&
710
method != http.MethodOptions &&
811
method != http.MethodConnect &&
912
method != http.MethodTrace
1013
}
14+
15+
func containsItem(infos []os.FileInfo, name string) bool {
16+
for i := range infos {
17+
if infos[i].Name() == name {
18+
return true
19+
}
20+
}
21+
return false
22+
}

0 commit comments

Comments
 (0)