Skip to content
8 changes: 6 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Will generate executable file "main" in current directory.
Start server on port 8080, root directory is current working directory:
```sh
ghfs -l 8080
```
```

Start server on port 8080, root directory is /usr/share/doc:
```sh
Expand Down Expand Up @@ -265,6 +265,10 @@ ghfs [options]
--archive-dir <fs-path> ...
--archive-dir-user <separator><fs-path>[<separator><allowed-username>...] ...
Similar to --archive, but use file system path instead of url path.
--archive-workers-max <number>
Maximum number of concurrent archive operations.
Set to 0 for unlimited (default).
When the limit is reached, new archive requests will be rejected with status code 429(Too Many Requests).

--global-cors
Allow CORS requests for all url path.
Expand Down Expand Up @@ -304,7 +308,7 @@ ghfs [options]
-S|--show <wildcard> ...
-SD|--show-dir <wildcard> ...
-SF|--show-file <wildcard> ...
If specified, files or directories match wildcards(except hidden by hide option) will be shown.
If specified, files or directories match wildcards(except hidden by hide option) will be shown.

-H|--hide <wildcard> ...
-HD|--hide-dir <wildcard> ...
Expand Down
6 changes: 5 additions & 1 deletion README.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ go build main.go
在8080端口启动服务器,根目录为当前工作目录:
```sh
ghfs -l 8080
```
```

在8080端口启动服务器,根目录为 /usr/share/doc:
```sh
Expand Down Expand Up @@ -255,6 +255,10 @@ ghfs [选项]
--archive-dir <文件系统路径> ...
--archive-dir-user <分隔符><文件系统路径>[<分隔符><允许的用户名>...] ...
与--archive类似,但指定的是文件系统路径,而不是URL路径。
--archive-workers-max <数值>
指定打包下载的最大并发数。
设为0(默认)表示无限制。
达到并发上限后,会拒绝后续打包请求并返回状态码429(Too Many Requests)。

--global-cors
接受所有URL路径的CORS跨域请求。
Expand Down
6 changes: 6 additions & 0 deletions src/param/cli.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,9 @@ func NewCliCmd() *goNixArgParser.Command {
err = options.AddFlagValues("archivedirsusers", "--archive-dir-user", "", nil, "file system path that allow archive files for specific users, <sep><fs-path>[<sep><user>...]")
serverError.CheckFatal(err)

err = options.AddFlagValue("archiveworkersmax", "--archive-workers-max", "", "0", "maximum number of concurrent archive operations (0 for unlimited)")
serverError.CheckFatal(err)

err = options.AddFlag("globalcors", "--global-cors", "GHFS_GLOBAL_CORS", "enable CORS headers for all directories")
serverError.CheckFatal(err)

Expand Down Expand Up @@ -436,6 +439,8 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e
archiveDirsUsers, _ := result.GetStrings("archivedirsusers")
param.ArchiveDirsUsers = SplitAllKeyValues(archiveDirsUsers)

param.ArchiveWorkersMax, _ = result.GetUint32("archiveworkersmax")

// global restrict access
if result.HasKey("globalrestrictaccess") {
param.GlobalRestrictAccess, _ = result.GetStrings("globalrestrictaccess")
Expand Down Expand Up @@ -465,6 +470,7 @@ func CmdResultsToParams(results []*goNixArgParser.ParseResult) (params Params, e
certFiles, _ := result.GetStrings("certs")
keyFiles, _ := result.GetStrings("keys")
param.CertKeyPaths, es = goVirtualHost.CertsKeysToPairs(certFiles, keyFiles)
errs = append(errs, es...)

// listen
listens, _ := result.GetStrings("listens")
Expand Down
11 changes: 6 additions & 5 deletions src/param/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,12 @@ type Param struct {
DeleteDirs []string
DeleteDirsUsers [][]string // [][path, user...]

GlobalArchive bool
ArchiveUrls []string
ArchiveUrlsUsers [][]string // [][path, user...]
ArchiveDirs []string
ArchiveDirsUsers [][]string // [][path, user...]
GlobalArchive bool
ArchiveUrls []string
ArchiveUrlsUsers [][]string // [][path, user...]
ArchiveDirs []string
ArchiveDirsUsers [][]string // [][path, user...]
ArchiveWorkersMax uint32

GlobalCors bool
CorsUrls []string
Expand Down
23 changes: 8 additions & 15 deletions src/serverHandler/aliasHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ type aliasHandler struct {
archive *hierarchyAvailability
cors *hierarchyAvailability

archiveWorkersMax uint32
archivingWorkers *uint32

globalRestrictAccess []string
restrictAccessUrls pathStringsList
restrictAccessDirs pathStringsList
Expand Down Expand Up @@ -120,21 +123,8 @@ func (h *aliasHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {

if session.isMutate && h.mutate(w, r, session, data) {
return
} else if session.isArchive {
switch session.archiveFormat {
case tarFmt:
if h.tar(w, r, session, data) {
return
}
case tgzFmt:
if h.tgz(w, r, session, data) {
return
}
case zipFmt:
if h.zip(w, r, session, data) {
return
}
}
} else if session.isArchive && h.tryArchive(w, r, session, data) {
return
}
}

Expand Down Expand Up @@ -179,6 +169,9 @@ func newAliasHandler(
toHttpsPort: p.ToHttpsPort,
defaultSort: p.DefaultSort,

archiveWorkersMax: p.ArchiveWorkersMax,
archivingWorkers: vhostCtx.archivingWorkers,

users: vhostCtx.users,
theme: vhostCtx.theme,
logger: vhostCtx.logger,
Expand Down
35 changes: 35 additions & 0 deletions src/serverHandler/archive.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"os"
"path"
"strings"
"sync/atomic"
)

type archiveCallback func(f *os.File, fInfo os.FileInfo, relPath string) error
Expand Down Expand Up @@ -210,3 +211,37 @@ func (h *aliasHandler) normalizeArchiveSelections(r *http.Request) ([]string, bo

return selections, true
}

func (h *aliasHandler) startArchive(w http.ResponseWriter, r *http.Request, session *sessionContext, data *responseData) (ok bool) {
switch session.archiveFormat {
case tarFmt:
return h.tar(w, r, session, data)
case tgzFmt:
return h.tgz(w, r, session, data)
case zipFmt:
return h.zip(w, r, session, data)
}

return
}

func (h *aliasHandler) tryArchive(w http.ResponseWriter, r *http.Request, session *sessionContext, data *responseData) (ok bool) {
if h.archivingWorkers == nil {
return h.startArchive(w, r, session, data)
}

if *h.archivingWorkers >= h.archiveWorkersMax {
data.Status = http.StatusTooManyRequests
return
}

archiving := atomic.AddUint32(h.archivingWorkers, 1)
ok = archiving <= h.archiveWorkersMax
if ok {
ok = h.startArchive(w, r, session, data)
} else {
data.Status = http.StatusTooManyRequests
}
atomic.AddUint32(h.archivingWorkers, ^uint32(0)) // archiveWorkers -= 1
Copy link

@OlegChuev OlegChuev May 1, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can ^uint32(0) cause uint underflow? (0 - 1 -> 4294967295)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think in this case, this would never happened since it always added by 1 first.

return
}
9 changes: 9 additions & 0 deletions src/serverHandler/vhostHandler.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ type vhostContext struct {
archiveUrlsUsers pathIntsList
archiveDirsUsers pathIntsList

archivingWorkers *uint32

shows *regexp.Regexp
showDirs *regexp.Regexp
showFiles *regexp.Regexp
Expand Down Expand Up @@ -95,6 +97,11 @@ func NewVhostHandler(
theme = defaultTheme.DefaultTheme
}

var archivingWorkers *uint32
if p.ArchiveWorkersMax > 0 {
archivingWorkers = new(uint32)
}

// alias param
vhostCtx := &vhostContext{
logger: logger,
Expand All @@ -114,6 +121,8 @@ func NewVhostHandler(
archiveUrlsUsers: pathUsernamesToPathUids(users, p.ArchiveUrlsUsers),
archiveDirsUsers: pathUsernamesToPathUids(users, p.ArchiveDirsUsers),

archivingWorkers: archivingWorkers,

shows: shows,
showDirs: showDirs,
showFiles: showFiles,
Expand Down