-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
squashed these commit real syscall.O_XX flag values abstract away differences in filesystem support best effort. Windows is not working upgrade action versions to avoid deprecation warnings update windows stat functions fix issues with mode on windows impl more windows testing remove console log
- Loading branch information
Showing
11 changed files
with
628 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
//go:build darwin || linux | ||
|
||
package filesys | ||
|
||
func FdType(fd int) int { | ||
return fd | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package filesys | ||
|
||
import "syscall" | ||
|
||
func FdType(fd int) syscall.Handle { | ||
return syscall.Handle(fd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,293 @@ | ||
package filesys | ||
|
||
import ( | ||
"encoding/base64" | ||
"encoding/json" | ||
"log" | ||
"net/http" | ||
"os" | ||
"strings" | ||
"syscall" | ||
) | ||
|
||
// FsHandler translates json payload data to and from system calls like syscall.Stat | ||
type FsHandler struct { | ||
debug bool | ||
securityToken string | ||
logger *log.Logger | ||
} | ||
|
||
func NewHandler(securityToken string, logger *log.Logger) *FsHandler { | ||
return &FsHandler{ | ||
debug: os.Getenv("DEBUG_FS_HANDLER") != "", | ||
securityToken: securityToken, | ||
logger: logger, | ||
} | ||
} | ||
|
||
func (fa *FsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { | ||
if r.Header.Get("WBT-Token") != fa.securityToken { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
switch r.URL.Path { | ||
case "/fs/stat": | ||
fa.handle(&Stat{}, w, r) | ||
case "/fs/fstat": | ||
fa.handle(&Fstat{}, w, r) | ||
case "/fs/open": | ||
fa.handle(&Open{}, w, r) | ||
case "/fs/write": | ||
fa.handle(&Write{}, w, r) | ||
case "/fs/close": | ||
fa.handle(&Close{}, w, r) | ||
case "/fs/rename": | ||
fa.handle(&Rename{}, w, r) | ||
case "/fs/readdir": | ||
fa.handle(&Readdir{}, w, r) | ||
case "/fs/lstat": | ||
fa.handle(&Lstat{}, w, r) | ||
case "/fs/read": | ||
fa.handle(&Read{}, w, r) | ||
case "/fs/mkdir": | ||
fa.handle(&Mkdir{}, w, r) | ||
case "/fs/unlink": | ||
fa.handle(&Unlink{}, w, r) | ||
case "/fs/rmdir": | ||
fa.handle(&Rmdir{}, w, r) | ||
default: | ||
fa.doError("not implemented", "ENOSYS", w) | ||
} | ||
} | ||
|
||
type Responder interface { | ||
WriteResponse(fa *FsHandler, w http.ResponseWriter) | ||
} | ||
|
||
func (fa *FsHandler) handle(responder Responder, w http.ResponseWriter, r *http.Request) { | ||
if err := json.NewDecoder(r.Body).Decode(responder); err != nil { | ||
if fa.debug { | ||
fa.logger.Printf("ERROR handle : %s\n", err) | ||
} | ||
w.WriteHeader(http.StatusBadRequest) | ||
return | ||
} | ||
if fa.debug { | ||
fa.logger.Printf("handle %s %+v\n", r.URL.Path, responder) | ||
} | ||
responder.WriteResponse(fa, w) | ||
} | ||
|
||
type ErrorCode struct { | ||
Error string `json:"error"` | ||
Code string `json:"code"` | ||
} | ||
|
||
func (fa *FsHandler) doError(msg, code string, w http.ResponseWriter) { | ||
if fa.debug { | ||
fa.logger.Printf("doError %s : %s\n", msg, code) | ||
} | ||
e := &ErrorCode{Error: msg, Code: code} | ||
|
||
w.WriteHeader(http.StatusBadRequest) | ||
w.Header().Set("Content-Type", "application/json") | ||
if err := json.NewEncoder(w).Encode(e); err != nil { | ||
fa.logger.Println("doError", err) | ||
} | ||
} | ||
|
||
func (fa *FsHandler) okResponse(data any, w http.ResponseWriter) { | ||
if marshal, err := json.Marshal(data); err != nil { | ||
fa.logger.Println("okResponse :", err) | ||
w.WriteHeader(http.StatusInternalServerError) | ||
|
||
} else { | ||
if fa.debug { | ||
fa.logger.Printf("okResponse %s\n", string(marshal)) | ||
} | ||
|
||
w.WriteHeader(http.StatusOK) | ||
w.Header().Set("Content-Type", "application/json") | ||
_, _ = w.Write(marshal) | ||
} | ||
} | ||
|
||
func fixPath(path string) string { | ||
return strings.TrimPrefix(path, "/fs/") | ||
} | ||
|
||
type Stat struct { | ||
Path string `json:"path,omitempty"` | ||
} | ||
|
||
type Open struct { | ||
Path string `json:"path"` | ||
Flags int `json:"flags"` | ||
Mode uint32 `json:"mode"` | ||
} | ||
|
||
func (o *Open) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
fd, err := syscall.Open(fixPath(o.Path), o.Flags, o.Mode) | ||
if err != nil { | ||
fa.doError(err.Error(), "ENOSYS", w) | ||
return | ||
} | ||
response := map[string]any{"fd": fd} | ||
fa.okResponse(response, w) | ||
} | ||
|
||
type Fstat struct { | ||
Fd int `json:"fd"` | ||
} | ||
|
||
type Write struct { | ||
Fd int `json:"fd"` | ||
Buffer string `json:"buffer"` | ||
Offset int `json:"offset"` | ||
Length int `json:"length"` | ||
Position *int `json:"position,omitempty"` | ||
} | ||
|
||
func (wr *Write) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
|
||
if wr.Position != nil || wr.Offset != 0 { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
|
||
bytes, err := base64.StdEncoding.DecodeString(wr.Buffer) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
|
||
var written int | ||
written, err = syscall.Write(FdType(wr.Fd), bytes) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
|
||
fa.okResponse(map[string]any{"written": written}, w) | ||
} | ||
|
||
type Close struct { | ||
Fd int `json:"fd"` | ||
} | ||
|
||
func (c *Close) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
err := syscall.Close(FdType(c.Fd)) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
fa.okResponse(map[string]any{}, w) | ||
} | ||
|
||
type Rename struct { | ||
From string `json:"from"` | ||
To string `json:"to"` | ||
} | ||
|
||
func (r *Rename) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
err := syscall.Rename(fixPath(r.From), fixPath(r.To)) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
} | ||
fa.okResponse(map[string]any{}, w) | ||
} | ||
|
||
type Readdir struct { | ||
Path string `json:"path"` | ||
} | ||
|
||
func (r *Readdir) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
entries, err := os.ReadDir(fixPath(r.Path)) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
stringNames := make([]string, 0) | ||
for _, entry := range entries { | ||
stringNames = append(stringNames, entry.Name()) | ||
} | ||
fa.okResponse(map[string]any{"entries": stringNames}, w) | ||
} | ||
|
||
type Lstat struct { | ||
Path string `json:"path"` | ||
} | ||
|
||
type Read struct { | ||
Fd int `json:"fd"` | ||
Offset int `json:"offset"` | ||
Length int `json:"length"` | ||
Position *int `json:"position,omitempty"` | ||
} | ||
|
||
func (r *Read) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
if r.Offset != 0 { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
if r.Position != nil { | ||
_, err := syscall.Seek(FdType(r.Fd), int64(*r.Position), 0) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
} | ||
|
||
buffer := make([]byte, r.Length) | ||
read, err := syscall.Read(FdType(r.Fd), buffer) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
response := map[string]any{ | ||
"read": read, | ||
"buffer": base64.StdEncoding.EncodeToString(buffer[:read]), | ||
} | ||
fa.okResponse(response, w) | ||
|
||
} | ||
|
||
type Mkdir struct { | ||
Path string `json:"path"` | ||
Perm uint32 `json:"perm"` | ||
} | ||
|
||
func (m *Mkdir) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
err := syscall.Mkdir(fixPath(m.Path), m.Perm) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
fa.okResponse(map[string]any{}, w) | ||
} | ||
|
||
type Unlink struct { | ||
Path string `json:"path"` | ||
} | ||
|
||
func (u *Unlink) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
err := syscall.Unlink(fixPath(u.Path)) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
fa.okResponse(map[string]any{}, w) | ||
} | ||
|
||
type Rmdir struct { | ||
Path string `json:"path"` | ||
} | ||
|
||
func (r *Rmdir) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
err := syscall.Rmdir(fixPath(r.Path)) | ||
if err != nil { | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
fa.okResponse(map[string]any{}, w) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
//go:build darwin || linux | ||
|
||
package filesys | ||
|
||
import ( | ||
"net/http" | ||
"os" | ||
"syscall" | ||
) | ||
|
||
func (st *Stat) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
s := &syscall.Stat_t{} | ||
err := syscall.Stat(fixPath(st.Path), s) | ||
if err != nil { | ||
if os.IsNotExist(err) { | ||
fa.doError(err.Error(), "ENOENT", w) | ||
return | ||
} | ||
fa.doError(err.Error(), "ENOSYS", w) | ||
return | ||
} | ||
fa.okResponse(mapOfStatT(s), w) | ||
} | ||
|
||
func (f *Fstat) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
s := &syscall.Stat_t{} | ||
err := syscall.Fstat(f.Fd, s) | ||
if err != nil { | ||
fa.doError(err.Error(), "ENOSYS", w) | ||
return | ||
} | ||
fa.okResponse(mapOfStatT(s), w) | ||
} | ||
|
||
func (ls *Lstat) WriteResponse(fa *FsHandler, w http.ResponseWriter) { | ||
s := &syscall.Stat_t{} | ||
err := syscall.Lstat(fixPath(ls.Path), s) | ||
if err != nil { | ||
if os.IsNotExist(err) { | ||
fa.doError(err.Error(), "ENOENT", w) | ||
return | ||
} | ||
fa.doError("not implemented", "ENOSYS", w) | ||
return | ||
} | ||
fa.okResponse(mapOfStatT(s), w) | ||
} |
Oops, something went wrong.