Skip to content

Commit

Permalink
api,disk: add filesystem endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
n8maninger committed Apr 11, 2023
1 parent b76936c commit be58a93
Show file tree
Hide file tree
Showing 10 changed files with 155 additions and 3 deletions.
1 change: 1 addition & 0 deletions api/api.go
Expand Up @@ -139,6 +139,7 @@ func NewServer(hostKey types.PublicKey, g Syncer, chain ChainManager, cm Contrac
"GET /wallet/transactions": a.handleGETWalletTransactions,
"GET /wallet/pending": a.handleGETWalletPending,
"POST /wallet/send": a.handlePOSTWalletSend,
"GET /system/dir/:path": a.handleGETSystemDir,
})
return r
}
36 changes: 36 additions & 0 deletions api/endpoints.go
Expand Up @@ -5,13 +5,15 @@ import (
"errors"
"fmt"
"net/http"
"os"
"time"

"go.sia.tech/core/types"
"go.sia.tech/hostd/host/contracts"
"go.sia.tech/hostd/host/metrics"
"go.sia.tech/hostd/host/settings"
"go.sia.tech/hostd/host/storage"
"go.sia.tech/hostd/internal/disk"
"go.sia.tech/jape"
"go.sia.tech/siad/modules"
"go.uber.org/zap"
Expand Down Expand Up @@ -354,6 +356,40 @@ func (a *API) handlePOSTWalletSend(c jape.Context) {
c.Error(errors.New("not implemented"), http.StatusInternalServerError)
}

func (a *API) handleGETSystemDir(c jape.Context) {
var path string
if err := c.DecodeParam("path", &path); err != nil {
c.Error(fmt.Errorf("failed to parse path: %w", err), http.StatusBadRequest)
} else if len(path) == 0 {
path, _ = os.UserHomeDir()
}

dir, err := os.ReadDir(path)
if errors.Is(err, os.ErrNotExist) {
c.Error(fmt.Errorf("path does not exist: %w", err), http.StatusBadRequest)
return
} else if !a.checkServerError(c, "failed to read dir", err) {
return
}

free, total, err := disk.Usage(path)
if !a.checkServerError(c, "failed to get disk usage", err) {
return
}

resp := SystemDirResponse{
FreeBytes: free,
TotalBytes: total,
}

for _, entry := range dir {
if entry.IsDir() {
resp.Directories = append(resp.Directories, entry.Name())
}
}
c.Encode(resp)
}

func parseLimitParams(c jape.Context, defaultLimit, maxLimit int) (limit, offset int) {
c.DecodeForm("limit", &limit)
c.DecodeForm("offset", &offset)
Expand Down
48 changes: 48 additions & 0 deletions api/openapi.yml
Expand Up @@ -273,6 +273,36 @@ paths:
type: array
items:
$ref: '#/components/schemas/Transaction'
/system/dir/{path}:
get:
summary: Retrieve system directory information
operationId: getSystemDir
parameters:
- name: path
in: path
required: true
schema:
type: string
description: The path of the directory to retrieve information about
responses:
'200':
description: Successfully retrieved directory information
content:
application/json:
schema:
$ref: '#/components/schemas/SystemDirResponse'
'400':
description: Bad request (invalid path)
content:
application/json:
schema:
$ref: '#/components/schemas/Error'
'500':
description: Internal server error
content:
application/json:
schema:
$ref: '#/components/schemas/Error'

components:
securitySchemes:
Expand Down Expand Up @@ -582,4 +612,22 @@ components:
confirmed:
type: string
unconfirmed:
type: string
SystemDirResponse:
type: object
properties:
totalBytes:
type: integer
format: uint64
freeBytes:
type: integer
format: uint64
directories:
type: array
items:
type: string
Error:
type: object
properties:
error:
type: string
7 changes: 7 additions & 0 deletions api/types.go
Expand Up @@ -80,6 +80,13 @@ type (
// UpdateSettingsRequest is the request body for the [PUT] /settings
// endpoint. It will be merged with the current settings.
UpdateSettingsRequest map[string]any

// SystemDirResponse is the response body for the [GET] /system/dir endpoint.
SystemDirResponse struct {
TotalBytes uint64 `json:"totalBytes"`
FreeBytes uint64 `json:"freeBytes"`
Directories []string `json:"directories"`
}
)

// WithAcceptingContracts sets the AcceptingContracts field of the request
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Expand Up @@ -14,6 +14,7 @@ require (
go.sia.tech/siad v1.5.10-0.20230228235644-3059c0b930ca
go.sia.tech/web/hostd v0.0.0-20230330233636-9ae27b2629a4
go.uber.org/zap v1.24.0
golang.org/x/sys v0.7.0
golang.org/x/term v0.5.0
golang.org/x/time v0.3.0
lukechampine.com/frand v1.4.2
Expand Down Expand Up @@ -60,7 +61,6 @@ require (
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/crypto v0.5.0 // indirect
golang.org/x/net v0.7.0 // indirect
golang.org/x/sys v0.5.0 // indirect
golang.org/x/text v0.7.0 // indirect
golang.org/x/tools v0.5.0 // indirect
google.golang.org/genproto v0.0.0-20221118155620-16455021b5e6 // indirect
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Expand Up @@ -479,8 +479,8 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0 h1:MUK/U/4lj1t1oPg0HfuXDN/Z1wv31ZJ/YcPiGccS4DU=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210421210424-b80969c67360/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand Down
2 changes: 2 additions & 0 deletions internal/disk/disk.go
@@ -0,0 +1,2 @@
// Package disk provides cross platform disk usage information
package disk
18 changes: 18 additions & 0 deletions internal/disk/disk_test.go
@@ -0,0 +1,18 @@
package disk

import (
"os"
"testing"
)

func TestUsage(t *testing.T) {
dir, err := os.UserHomeDir()
if err != nil {
t.Fatal(err)
}
free, total, err := Usage(dir)
if err != nil {
t.Fatal(err)
}
t.Logf("%s - %d/%d bytes", dir, free, total)
}
15 changes: 15 additions & 0 deletions internal/disk/usage_default.go
@@ -0,0 +1,15 @@
//go:build !windows

package disk

import "syscall"

// Usage returns the free and total bytes on the filesystem containing the
// specified path.
func Usage(p string) (free, total uint64, err error) {
var stat syscall.Statfs_t
if err := syscall.Statfs(p, &stat); err != nil {
return 0, 0, err
}
return stat.Bfree * uint64(stat.Bsize), stat.Blocks * uint64(stat.Bsize), nil
}
25 changes: 25 additions & 0 deletions internal/disk/usage_windows.go
@@ -0,0 +1,25 @@
//go:build windows

package disk

import (
"fmt"
"os"

"golang.org/x/sys/windows"
)

// Usage returns the free and totreal bytes on the filesystem containing the
// specified path.
func Usage(p string) (free, total uint64, err error) {
if _, err = os.Stat(p); err != nil {
return 0, 0, fmt.Errorf("failed to stat %v: %v", p, err)
}

ptr, err := windows.UTF16PtrFromString(p)
if err != nil {
panic(err)
}
err = windows.GetDiskFreeSpaceEx(ptr, &free, &total, nil)
return
}

0 comments on commit be58a93

Please sign in to comment.