]> Sergey Matveev's repositories - btrtrc.git/commitdiff
Harden read/write part file ordering
authorMatt Joiner <anacrolix@gmail.com>
Tue, 6 May 2025 23:37:25 +0000 (09:37 +1000)
committerMatt Joiner <anacrolix@gmail.com>
Tue, 6 May 2025 23:37:25 +0000 (09:37 +1000)
storage/file-piece.go
storage/file.go

index 985cca924b50536eda940cd9f5686dd1c5bac4bd..7b25fdd33ff344c10ee1ff3797981b9aa26ea869 100644 (file)
@@ -64,9 +64,9 @@ func (me *filePieceImpl) Completion() Completion {
                for i, extent := range me.t.segmentLocater.LocateIter(me.extent()) {
                        noFiles = false
                        file := me.t.files[i]
-                       s, err := os.Stat(file.safeOsPath)
+                       s, err := os.Stat(file.partFilePath())
                        if errors.Is(err, fs.ErrNotExist) {
-                               s, err = os.Stat(file.partFilePath())
+                               s, err = os.Stat(file.safeOsPath)
                        }
                        if err != nil {
                                me.logger().Warn(
@@ -185,7 +185,7 @@ func (me *filePieceImpl) onFileNotComplete(f file) (err error) {
        // Ensure the file is writable
        err = os.Chmod(f.safeOsPath, info.Mode().Perm()|(filePerm&0o222))
        if err != nil {
-               err = fmt.Errorf("setting file to read-only: %w", err)
+               err = fmt.Errorf("setting file writable: %w", err)
                return
        }
        return
index 42ef20c55df8c8a0e3ac0502df02f5a1f60c5c3f..3cf5f62b14a01cea390f760a42e3f478638d6b7f 100644 (file)
@@ -222,10 +222,13 @@ type fileTorrentImplIO struct {
 
 // Returns EOF on short or missing file.
 func (fst fileTorrentImplIO) readFileAt(file file, b []byte, off int64) (n int, err error) {
-       f, err := os.Open(file.safeOsPath)
-       if fst.fts.partFiles() && errors.Is(err, fs.ErrNotExist) {
+       var f *os.File
+       if fst.fts.partFiles() {
                f, err = os.Open(file.partFilePath())
        }
+       if errors.Is(err, fs.ErrNotExist) {
+               f, err = os.Open(file.safeOsPath)
+       }
        if errors.Is(err, fs.ErrNotExist) {
                // File missing is treated the same as a short file. Should we propagate this through the
                // interface now that fs.ErrNotExist is a thing?
@@ -268,13 +271,32 @@ func (fst fileTorrentImplIO) ReadAt(b []byte, off int64) (n int, err error) {
        return
 }
 
+func (fst fileTorrentImplIO) openForWrite(file file) (f *os.File, err error) {
+       p := fst.fts.pathForWrite(file)
+       f, err = os.OpenFile(p, os.O_WRONLY|os.O_CREATE, filePerm)
+       if err == nil {
+               return
+       }
+       if errors.Is(err, fs.ErrNotExist) {
+               err = os.MkdirAll(filepath.Dir(p), dirPerm)
+               if err != nil {
+                       return
+               }
+       } else if errors.Is(err, fs.ErrPermission) {
+               err = os.Chmod(p, filePerm)
+               if err != nil {
+                       return
+               }
+       }
+       f, err = os.OpenFile(p, os.O_WRONLY|os.O_CREATE, filePerm)
+       return
+}
+
 func (fst fileTorrentImplIO) WriteAt(p []byte, off int64) (n int, err error) {
        // log.Printf("write at %v: %v bytes", off, len(p))
        fst.fts.segmentLocater.Locate(segments.Extent{off, int64(len(p))}, func(i int, e segments.Extent) bool {
-               name := fst.fts.pathForWrite(fst.fts.files[i])
-               os.MkdirAll(filepath.Dir(name), dirPerm)
                var f *os.File
-               f, err = os.OpenFile(name, os.O_WRONLY|os.O_CREATE, filePerm)
+               f, err = fst.openForWrite(fst.fts.files[i])
                if err != nil {
                        return false
                }