Skip to content

Commit bd60316

Browse files
committed
some initial cleanup to account for drift
1 parent cdc6472 commit bd60316

18 files changed

Lines changed: 106 additions & 85 deletions

environment/settings.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,9 @@ func (l Limits) AsContainerResources() container.Resources {
107107
Memory: l.BoundedMemoryLimit(),
108108
MemoryReservation: l.MemoryLimit * 1024 * 1024,
109109
MemorySwap: l.ConvertedSwap(),
110-
BlkioWeight: l.IoWeight,
111-
OomKillDisable: &l.OOMDisabled,
112-
PidsLimit: &pids,
110+
// BlkioWeight: l.IoWeight,
111+
OomKillDisable: &l.OOMDisabled,
112+
PidsLimit: &pids,
113113
}
114114

115115
// If the CPU Limit is not set, don't send any of these fields through. Providing

router/downloader/downloader.go

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -199,13 +199,8 @@ func (dl *Download) Execute() error {
199199
return errors.New("downloader: got bad response status from endpoint: " + res.Status)
200200
}
201201

202-
// If there is a Content-Length header on this request go ahead and check that we can
203-
// even write the whole file before beginning this process. If there is no header present
204-
// we'll just have to give it a spin and see how it goes.
205-
if res.ContentLength > 0 {
206-
if err := dl.server.Filesystem().HasSpaceFor(res.ContentLength); err != nil {
207-
return errors.WrapIf(err, "downloader: failed to write file: not enough space")
208-
}
202+
if res.ContentLength < 1 {
203+
return errors.New("downloader: request is missing ContentLength")
209204
}
210205

211206
if dl.req.UseHeader {
@@ -232,8 +227,10 @@ func (dl *Download) Execute() error {
232227
p := dl.Path()
233228
dl.server.Log().WithField("path", p).Debug("writing remote file to disk")
234229

230+
// Write the file while tracking the progress, Write will check that the
231+
// size of the file won't exceed the disk limit.
235232
r := io.TeeReader(res.Body, dl.counter(res.ContentLength))
236-
if err := dl.server.Filesystem().Writefile(p, r); err != nil {
233+
if err := dl.server.Filesystem().Write(p, r, res.ContentLength, 0o644); err != nil {
237234
return errors.WrapIf(err, "downloader: failed to write file to server directory")
238235
}
239236
return nil

router/router_download.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ func getDownloadBackup(c *gin.Context) {
5656
return
5757
}
5858

59+
// The use of `os` here is safe as backups are not stored within server access
60+
// directories, and this path is program-controlled, not user input.
5961
f, err := os.Open(b.Path())
6062
if err != nil {
6163
middleware.CaptureAndAbort(c, err)

router/router_server.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -225,13 +225,18 @@ func deleteServer(c *gin.Context) {
225225
// done in a separate process since failure is not the end of the world and can be
226226
// manually cleaned up after the fact.
227227
//
228-
// In addition, servers with large amounts of files can take some time to finish deleting,
228+
// In addition, servers with large numbers of files can take some time to finish deleting,
229229
// so we don't want to block the HTTP call while waiting on this.
230+
p := s.Filesystem().Path()
230231
go func(p string) {
231232
if err := os.RemoveAll(p); err != nil {
232233
log.WithFields(log.Fields{"path": p, "error": err}).Warn("failed to remove server files during deletion process")
233234
}
234-
}(s.Filesystem().Path())
235+
}(p)
236+
237+
if err := s.Filesystem().Close(); err != nil {
238+
log.WithFields(log.Fields{"server": s.ID(), "error": err}).Warn("failed to close filesystem root")
239+
}
235240

236241
middleware.ExtractManager(c).Remove(func(server *server.Server) bool {
237242
return server.ID() == s.ID()

router/router_server_files.go

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,7 @@ import (
3030
// getServerFileContents returns the contents of a file on the server.
3131
func getServerFileContents(c *gin.Context) {
3232
s := middleware.ExtractServer(c)
33-
p := "/" + strings.TrimLeft(c.Query("file"), "/")
34-
f, st, err := s.Filesystem().File(p)
33+
f, st, err := s.Filesystem().File(c.Query("file"))
3534
if err != nil {
3635
middleware.CaptureAndAbort(c, err)
3736
return
@@ -248,7 +247,7 @@ func postServerWriteFile(c *gin.Context) {
248247
return
249248
}
250249

251-
if err := s.Filesystem().Writefile(f, c.Request.Body); err != nil {
250+
if err := s.Filesystem().Write(f, c.Request.Body, c.Request.ContentLength, 0o644); err != nil {
252251
if filesystem.IsErrorCode(err, filesystem.ErrCodeIsDirectory) {
253252
c.AbortWithStatusJSON(http.StatusBadRequest, gin.H{
254253
"error": "Cannot write file, name conflicts with an existing directory by the same name.",
@@ -622,7 +621,7 @@ func handleFileUpload(p string, s *server.Server, header *multipart.FileHeader)
622621
if err := s.Filesystem().IsIgnored(p); err != nil {
623622
return err
624623
}
625-
if err := s.Filesystem().Writefile(p, file); err != nil {
624+
if err := s.Filesystem().Write(p, file, header.Size, 0o644); err != nil {
626625
return err
627626
}
628627
return nil

router/router_transfer.go

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,8 +106,11 @@ func postTransfers(c *gin.Context) {
106106
if !successful && err != nil {
107107
// Delete all extracted files.
108108
go func(trnsfr *transfer.Transfer) {
109-
if err := os.RemoveAll(trnsfr.Server.Filesystem().Path()); err != nil && !os.IsNotExist(err) {
110-
trnsfr.Log().WithError(err).Warn("failed to delete local server files")
109+
_ = trnsfr.Server.Filesystem().Close()
110+
if err := os.RemoveAll(trnsfr.Server.Filesystem().Path()); err != nil {
111+
if !errors.Is(err, os.ErrNotExist) {
112+
trnsfr.Log().WithError(err).Warn("failed to delete local server files")
113+
}
111114
}
112115
}(trnsfr)
113116
}

server/backup.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,17 +154,11 @@ func (s *Server) RestoreBackup(b backup.BackupInterface, reader io.ReadCloser) (
154154
err = b.Restore(s.Context(), reader, func(file string, info fs.FileInfo, r io.ReadCloser) error {
155155
defer r.Close()
156156
s.Events().Publish(DaemonMessageEvent, "(restoring): "+file)
157-
158-
if err := s.Filesystem().Writefile(file, r); err != nil {
159-
return err
160-
}
161-
if err := s.Filesystem().Chmod(file, info.Mode()); err != nil {
157+
if err := s.Filesystem().Write(file, r, info.Size(), info.Mode()); err != nil {
162158
return err
163159
}
164-
165160
atime := info.ModTime()
166-
mtime := atime
167-
return s.Filesystem().Chtimes(file, atime, mtime)
161+
return s.Filesystem().Chtimes(file, atime, atime)
168162
})
169163

170164
return errors.WithStackIf(err)

server/filesystem/archive.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package filesystem
33
import (
44
"archive/tar"
55
"context"
6-
"fmt"
76
"io"
87
"io/fs"
98
"os"
@@ -124,6 +123,13 @@ func (a *Archive) Progress() *progress.Progress {
124123
return a.p
125124
}
126125

126+
func (a *Archive) Close() error {
127+
if err := a.root.Close(); err != nil {
128+
return errors.Wrap(err, "server/filesystem: archive: failed to close root")
129+
}
130+
return nil
131+
}
132+
127133
// Create .
128134
func (a *Archive) Create(ctx context.Context, f *os.File) error {
129135
// Select a writer based off of the WriteLimit configuration option. If there is no
@@ -233,7 +239,6 @@ func (a *Archive) addToArchive(p string) error {
233239
if err != nil {
234240
target = ""
235241
}
236-
fmt.Println(p, " targeting ", target)
237242
}
238243

239244
// Get the tar FileInfoHeader to add the file to the archive.

server/filesystem/disk_space.go

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
package filesystem
22

33
import (
4+
fs2 "io/fs"
5+
"os"
46
"path/filepath"
5-
"strings"
7+
"slices"
68
"sync"
79
"sync/atomic"
810
"time"
911

1012
"emperror.dev/errors"
1113
"github.com/apex/log"
12-
"github.com/karrick/godirwalk"
14+
"golang.org/x/sys/unix"
1315
)
1416

1517
type SpaceCheckingOpts struct {
1618
AllowStaleResponse bool
1719
}
1820

21+
// TODO: can this be replaced with some sort of atomic? Like atomic.Pointer?
1922
type usageLookupTime struct {
2023
sync.RWMutex
2124
value time.Time
@@ -36,12 +39,13 @@ func (ult *usageLookupTime) Get() time.Time {
3639
return ult.value
3740
}
3841

39-
// Returns the maximum amount of disk space that this Filesystem instance is allowed to use.
42+
// MaxDisk returns the maximum amount of disk space that this Filesystem
43+
// instance is allowed to use.
4044
func (fs *Filesystem) MaxDisk() int64 {
4145
return atomic.LoadInt64(&fs.diskLimit)
4246
}
4347

44-
// Sets the disk space limit for this Filesystem instance.
48+
// SetDiskLimit sets the disk space limit for this Filesystem instance.
4549
func (fs *Filesystem) SetDiskLimit(i int64) {
4650
atomic.SwapInt64(&fs.diskLimit, i)
4751
}
@@ -66,7 +70,7 @@ func (fs *Filesystem) HasSpaceErr(allowStaleValue bool) error {
6670
func (fs *Filesystem) HasSpaceAvailable(allowStaleValue bool) bool {
6771
size, err := fs.DiskUsage(allowStaleValue)
6872
if err != nil {
69-
log.WithField("root", fs.root).WithField("error", err).Warn("failed to determine root fs directory size")
73+
log.WithField("root", fs.Path()).WithField("error", err).Warn("failed to determine root fs directory size")
7074
}
7175

7276
// If space is -1 or 0 just return true, means they're allowed unlimited.
@@ -115,7 +119,7 @@ func (fs *Filesystem) DiskUsage(allowStaleValue bool) (int64, error) {
115119
// currently performing a lookup, just do the disk usage calculation in the background.
116120
go func(fs *Filesystem) {
117121
if _, err := fs.updateCachedDiskUsage(); err != nil {
118-
log.WithField("root", fs.root).WithField("error", err).Warn("failed to update fs disk usage from within routine")
122+
log.WithField("root", fs.rootPath).WithField("error", err).Warn("failed to update fs disk usage from within routine")
119123
}
120124
}(fs)
121125
}
@@ -155,37 +159,55 @@ func (fs *Filesystem) updateCachedDiskUsage() (int64, error) {
155159
return size, err
156160
}
157161

158-
// Determines the directory size of a given location by running parallel tasks to iterate
159-
// through all of the folders. Returns the size in bytes. This can be a fairly taxing operation
160-
// on locations with tons of files, so it is recommended that you cache the output.
162+
// DirectorySize determines the directory size of a given location. Returns the size
163+
// in bytes. This can be a fairly taxing operation on locations with tons of files,
164+
// so it is recommended that you cache the output.
161165
func (fs *Filesystem) DirectorySize(dir string) (int64, error) {
162-
dir = strings.TrimLeft(filepath.Clean(dir), "/")
163-
if dir != "" {
166+
dir = normalize(dir)
167+
if dir != "." {
164168
if _, err := fs.root.Lstat(dir); err != nil {
165169
return 0, err
166170
}
167171
}
168172

173+
rt := fs.root
174+
if dir != "." {
175+
r, err := fs.root.OpenRoot(dir)
176+
if err != nil {
177+
return 0, errors.Wrap(err, "server/filesystem: directorysize: failed to open root directory")
178+
}
179+
defer r.Close()
180+
rt = r
181+
}
182+
169183
var size int64
170-
err := godirwalk.Walk(filepath.Join(fs.rootPath, dir), &godirwalk.Options{
171-
Unsorted: true,
172-
FollowSymbolicLinks: false,
173-
Callback: func(p string, e *godirwalk.Dirent) error {
174-
if !e.ModeType().IsRegular() {
184+
var links []uint64
185+
186+
err := filepath.WalkDir(rt.Name(), func(path string, d fs2.DirEntry, err error) error {
187+
if !d.Type().IsRegular() {
188+
return nil
189+
}
190+
191+
st, err := d.Info()
192+
if err != nil {
193+
if errors.Is(err, os.ErrNotExist) {
175194
return nil
176195
}
196+
return err
197+
}
177198

178-
if !e.IsDir() {
179-
st, err := fs.root.Lstat(strings.TrimLeft(strings.TrimPrefix(p, fs.rootPath), "/"))
180-
if err != nil {
181-
return errors.Wrap(err, "server/filesystem: directorysize: failed to stat file")
182-
}
183-
atomic.AddInt64(&size, st.Size())
199+
s := st.Sys().(*unix.Stat_t)
200+
if s.Nlink > 1 {
201+
// Hard links have the same inode number, don't add them more than once.
202+
if slices.Contains(links, s.Ino) {
203+
return nil
184204
}
205+
links = append(links, s.Ino)
206+
}
185207

186-
// todo: don't count hardlinks twice
187-
return nil
188-
},
208+
size += st.Size()
209+
210+
return nil
189211
})
190212

191213
return size, errors.WrapIf(err, "server/filesystem: directorysize: failed to walk directory")

server/filesystem/filesystem.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,14 @@ func (fs *Filesystem) Path() string {
7777
return fs.rootPath
7878
}
7979

80+
// Close closes the underlying os.Root instance for the server.
81+
func (fs *Filesystem) Close() error {
82+
if err := fs.root.Close(); err != nil {
83+
return errors.Wrap(err, "server/filesystem: failed to close root")
84+
}
85+
return nil
86+
}
87+
8088
// File returns a reader for a file instance as well as the stat information.
8189
func (fs *Filesystem) File(p string) (*os.File, Stat, error) {
8290
p = normalize(p)

0 commit comments

Comments
 (0)