commit 83695cea819b9173053a968f283d733c7eab7c09 Author: chfs-ng Date: Mon Sep 21 12:58:08 2026 +0800 chore: bootstrap CHFS NG workspace diff --git a/PlexPt b/PlexPt new file mode 160000 index 0000000..21afcc3 --- /dev/null +++ b/PlexPt @@ -0,0 +1 @@ +Subproject commit 21afcc303467f8aa04a3a3d8d0baa59e3d773732 diff --git a/chfs-ng/cmd/chfs/main.go b/chfs-ng/cmd/chfs/main.go new file mode 100644 index 0000000..492a17d --- /dev/null +++ b/chfs-ng/cmd/chfs/main.go @@ -0,0 +1,14 @@ +package main + +import ( + "chfs-ng/internal/api" + "chfs-ng/internal/storage" + "log" + "net/http" +) + +func main() { + s := &api.Server{Store: storage.Local{Root: "/data"}} + log.Println("chfs-ng development server listening on :80") + log.Fatal(http.ListenAndServe(":80", s.Handler())) +} diff --git a/chfs-ng/go.mod b/chfs-ng/go.mod new file mode 100644 index 0000000..66ce25b --- /dev/null +++ b/chfs-ng/go.mod @@ -0,0 +1,3 @@ +module chfs-ng + +go 1.22 diff --git a/chfs-ng/internal/api/server.go b/chfs-ng/internal/api/server.go new file mode 100644 index 0000000..883d626 --- /dev/null +++ b/chfs-ng/internal/api/server.go @@ -0,0 +1,26 @@ +package api + +import ( + "chfs-ng/internal/storage" + "net/http" +) + +type Server struct{ Store storage.Storage } + +func (s *Server) Handler() http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/chfs/files", s.files) + mux.HandleFunc("/chfs/newdir", s.newdir) + mux.HandleFunc("/chfs/rename", s.rename) + mux.HandleFunc("/chfs/rmfiles", s.remove) + mux.HandleFunc("/chfs/exist", s.exist) + mux.HandleFunc("/chfs/session", s.session) + return mux +} +func notImpl(w http.ResponseWriter) { http.Error(w, "not implemented", http.StatusNotImplemented) } +func (s *Server) files(w http.ResponseWriter, r *http.Request) { notImpl(w) } +func (s *Server) newdir(w http.ResponseWriter, r *http.Request) { notImpl(w) } +func (s *Server) rename(w http.ResponseWriter, r *http.Request) { notImpl(w) } +func (s *Server) remove(w http.ResponseWriter, r *http.Request) { notImpl(w) } +func (s *Server) exist(w http.ResponseWriter, r *http.Request) { notImpl(w) } +func (s *Server) session(w http.ResponseWriter, r *http.Request) { notImpl(w) } diff --git a/chfs-ng/internal/auth/auth.go b/chfs-ng/internal/auth/auth.go new file mode 100644 index 0000000..fba2da5 --- /dev/null +++ b/chfs-ng/internal/auth/auth.go @@ -0,0 +1,19 @@ +package auth + +type PermMask uint8 + +const ( + None PermMask = 0 + Read PermMask = 1 << iota + Write + Delete +) + +func (p PermMask) HasRead() bool { return p&Read != 0 } +func (p PermMask) HasWrite() bool { return p&Write != 0 } +func (p PermMask) HasDelete() bool { return p&Delete != 0 } + +type Account struct { + Username, Password string + Mask PermMask +} diff --git a/chfs-ng/internal/storage/storage.go b/chfs-ng/internal/storage/storage.go new file mode 100644 index 0000000..30d3cf5 --- /dev/null +++ b/chfs-ng/internal/storage/storage.go @@ -0,0 +1,40 @@ +package storage + +import ( + "os" + "path/filepath" +) + +type Storage interface { + Stat(path string) (os.FileInfo, error) + Open(path string) (*os.File, error) + Create(path string) (*os.File, error) + Remove(path string) error + Rename(oldPath, newPath string) error + MkdirAll(path string) error + List(path string) ([]os.FileInfo, error) +} + +type Local struct{ Root string } + +func (s Local) real(path string) string { return filepath.Join(s.Root, filepath.Clean("/"+path)) } +func (s Local) Stat(path string) (os.FileInfo, error) { return os.Stat(s.real(path)) } +func (s Local) Open(path string) (*os.File, error) { return os.Open(s.real(path)) } +func (s Local) Create(path string) (*os.File, error) { + p := s.real(path) + if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { + return nil, err + } + return os.Create(p) +} +func (s Local) Remove(path string) error { return os.RemoveAll(s.real(path)) } +func (s Local) Rename(a, b string) error { return os.Rename(s.real(a), s.real(b)) } +func (s Local) MkdirAll(path string) error { return os.MkdirAll(s.real(path), 0755) } +func (s Local) List(path string) ([]os.FileInfo, error) { + f, err := os.Open(s.real(path)) + if err != nil { + return nil, err + } + defer f.Close() + return f.Readdir(-1) +} diff --git a/compatibility/api-v1.md b/compatibility/api-v1.md new file mode 100644 index 0000000..2e82395 --- /dev/null +++ b/compatibility/api-v1.md @@ -0,0 +1,17 @@ +# CHFS v1 compatibility surface + +Keep legacy endpoints stable during migration. + +- session: login/logout +- files: directory listing +- shared: direct file access +- newdir: create directory +- rename: rename file/directory +- rmfiles: delete +- search: global search +- upload/upload2: upload +- newtext/updatetext: text file editing +- preview: preview +- exist: existence check +- downloaddir: ZIP directory download +- webdav: WebDAV diff --git a/compatibility/config-v1.md b/compatibility/config-v1.md new file mode 100644 index 0000000..50d836a --- /dev/null +++ b/compatibility/config-v1.md @@ -0,0 +1,18 @@ +# Known legacy configuration + +path=/data +image.preview=true +file.remove=3 + +Known additional keys from binary/config ecosystem: +session.timeout +folder.download +folder.leaf.download +rule.default +rule.r +rule.w +rule.d +html.title +html.notice +ssl.cert +ssl.key diff --git a/luguo b/luguo new file mode 160000 index 0000000..75ea2d5 --- /dev/null +++ b/luguo @@ -0,0 +1 @@ +Subproject commit 75ea2d59d6ae97dea9e5157b3833ceb0aafb8d22 diff --git a/original/chfs-v4.0beta.min.bin b/original/chfs-v4.0beta.min.bin new file mode 100755 index 0000000..80c2d9c Binary files /dev/null and b/original/chfs-v4.0beta.min.bin differ diff --git a/original/chfs-v4.0beta.min.ini b/original/chfs-v4.0beta.min.ini new file mode 100644 index 0000000..0f4fdc8 --- /dev/null +++ b/original/chfs-v4.0beta.min.ini @@ -0,0 +1,3 @@ +path=/data +image.preview=true +file.remove=3 \ No newline at end of file diff --git a/reverse/architecture.md b/reverse/architecture.md new file mode 100644 index 0000000..f4d100e --- /dev/null +++ b/reverse/architecture.md @@ -0,0 +1,20 @@ +# CHFS v4 reverse engineering notes + +Base image: docblue/chfs:v4.0beta.min +Go: 1.18.1 +Build revision: c430c39551e5e2e5c18aa57cdcc734814f795e4e +Build time: 2024-11-14T13:41:08Z +vcs.modified=true + +Recovered modules: +- chfs/account: account, permission, token +- chfs/homedir +- chfs/mylog/ops +- chfs/util/pajfile +- main: HTTP handlers, configuration, MulPathMgr, WebDAV, ignore filtering +- webdav: embedded WebDAV implementation + +Recovered HTTP endpoints include: +/chfs/session /chfs/files /chfs/newdir /chfs/rename /chfs/search /chfs/shared +/chfs/upload /chfs/upload2 /chfs/newtext /chfs/preview /chfs/rmfiles +/chfs/updatetext /chfs/exist /chfs/downloaddir/ /webdav diff --git a/reverse/functions.txt b/reverse/functions.txt new file mode 100644 index 0000000..5a6b356 --- /dev/null +++ b/reverse/functions.txt @@ -0,0 +1,100 @@ +main +chfs/account +chfs/account.init.0 +chfs/account.HasAccount +chfs/account.HasAccount.func1 +chfs/account.AddAccount +chfs/account.ValidateAccount +chfs/account.ValidateAccount.func1 +chfs/account.ValidatePermMask +chfs/account.PermMask.HasNonePermission +chfs/account.PermMask.HasReadPermission +chfs/account.PermMask.HasWritePermission +chfs/account.PermMask.HasDeletePermission +chfs/account.AccessMask +chfs/account.AccessMask.func1 +chfs/account.CreateToken +chfs/account.GetAccFromToken.func1 +chfs/account.GetAccFromToken +chfs/account.GetAccFromToken.func2 +chfs/account.(*PermMask).HasDeletePermission +chfs/account.(*PermMask).HasNonePermission +chfs/account.(*PermMask).HasReadPermission +chfs/account.(*PermMask).HasWritePermission +chfs/homedir.Dir +chfs/homedir.dirUnix +chfs/icons.GetFileIcon +chfs/mylog/ops.InitOpsLog +chfs/mylog/ops.Output +chfs/mylog/ops.Output.func1 +chfs/util/pajfile.IsFilePathExist +main.parseAllowAddresses +main.addIP +main.checkIPisAllow +main.login +main.getTokenFromCookies +main.logout +main.createZipImpl +main.createZipImpl.func1 +main.(*GitIgnore).MatchesPath +main.downloadDir +main.listfile +main.listfile.func2 +main.listfile.func1 +main.deletefile +main.deletefile.func1 +main.rename +main.createDir +main.newtext +main.updatetext +main.upload2 +main.upload2.func4 +main.upload2.func3 +main.upload2.func2 +main.upload2.func1 +main.upload +main.upload.func4 +main.upload.func3 +main.upload.func2 +main.upload.func1 +main.isexist +main.responseError +main.previewHandler.func1 +main.previewHandler +main.previewHandler.func2 +main.search +main.search.func2 +main.search.func1 +main.parseConfigurationFile +main.report +main.report.func1 +main.init.0 +main.CompileIgnoreLines +main.getPatternFromLine +main.(*GitIgnore).matchesPathHow +main.main.func1 +main.main +main.main.func3 +main.main.func2 +main.(*filterRouter).ServeHTTP +main.checkIPv4 +main.compareVer +main.splitArray2Rows +main.myRender +main.myRender.func1 +main.getLocalIPs +main.makeSites +main.getLatestVerFromServer +main.clientIP +main.whoami +main.parseCLI +main.initMulPathMgr +main.(*MulPathMgr).Find +main.(*MulPathMgr).extractFileName +main.(*MulPathMgr).realPath +main.(*MulPathMgr).matched +main.(*MulPathMgr).realPathForFile +main.(*MulPathMgr).peelRealRootPath +main.(*MulPathMgr).subRealRootPath +main.webdavHandler +main.init diff --git a/vlianx b/vlianx new file mode 160000 index 0000000..abe33d8 --- /dev/null +++ b/vlianx @@ -0,0 +1 @@ +Subproject commit abe33d893c0ef29c08a23e93619be94c5ddcb124