feat: Implement File.WriteAt

This commit is contained in:
Felicitas Pojtinger
2021-12-27 17:24:35 +01:00
parent cd07daf2b5
commit 9cfa7cf0f0

View File

@@ -264,6 +264,81 @@ func (f *File) enterWriteMode() error {
return nil return nil
} }
func (f *File) seekWithoutLocking(offset int64, whence int) (int64, error) {
log.Println("File.seekWithoutLocking", f.name, offset, whence)
if f.info.IsDir() {
return -1, ErrIsDirectory
}
if f.writeBuf != nil {
return f.writeBuf.Seek(offset, whence)
}
dst := int64(0)
switch whence {
case io.SeekStart:
dst = offset
case io.SeekCurrent:
curr := 0
if f.readOpReader != nil {
curr = f.readOpReader.BytesRead
}
dst = int64(curr) + offset
case io.SeekEnd:
dst = f.info.Size() - offset
default:
return -1, ErrNotImplemented
}
if f.readOpReader == nil || f.readOpWriter == nil || dst < int64(f.readOpReader.BytesRead) { // We have to re-open as we can't seek backwards
_ = f.closeWithoutLocking() // Ignore errors here as it might not be opened
r, writer := io.Pipe()
reader := &ioext.CounterReadCloser{
Reader: r,
BytesRead: 0,
}
go func() {
if err := f.readOps.Restore(
func(path string, mode fs.FileMode) (io.WriteCloser, error) {
return writer, nil
},
func(path string, mode fs.FileMode) error {
// Not necessary; can't read on a directory
return nil
},
f.path,
"",
true,
); err != nil {
if err == io.ErrClosedPipe {
return
}
// TODO: Handle error
panic(err)
}
}()
f.readOpReader = reader
f.readOpWriter = writer
}
written, err := io.CopyN(io.Discard, f.readOpReader, dst-int64(f.readOpReader.BytesRead))
if err == io.EOF {
return written, io.EOF
}
if err != nil {
return -1, err
}
return written, nil
}
// Inventory // Inventory
func (f *File) Name() string { func (f *File) Name() string {
log.Println("File.Name", f.name) log.Println("File.Name", f.name)
@@ -404,79 +479,10 @@ func (f *File) ReadAt(p []byte, off int64) (n int, err error) {
func (f *File) Seek(offset int64, whence int) (int64, error) { func (f *File) Seek(offset int64, whence int) (int64, error) {
log.Println("File.Seek", f.name, offset, whence) log.Println("File.Seek", f.name, offset, whence)
if f.info.IsDir() {
return -1, ErrIsDirectory
}
f.ioLock.Lock() f.ioLock.Lock()
defer f.ioLock.Unlock() defer f.ioLock.Unlock()
if f.writeBuf != nil { return f.seekWithoutLocking(offset, whence)
return f.writeBuf.Seek(offset, whence)
}
dst := int64(0)
switch whence {
case io.SeekStart:
dst = offset
case io.SeekCurrent:
curr := 0
if f.readOpReader != nil {
curr = f.readOpReader.BytesRead
}
dst = int64(curr) + offset
case io.SeekEnd:
dst = f.info.Size() - offset
default:
return -1, ErrNotImplemented
}
if f.readOpReader == nil || f.readOpWriter == nil || dst < int64(f.readOpReader.BytesRead) { // We have to re-open as we can't seek backwards
_ = f.closeWithoutLocking() // Ignore errors here as it might not be opened
r, writer := io.Pipe()
reader := &ioext.CounterReadCloser{
Reader: r,
BytesRead: 0,
}
go func() {
if err := f.readOps.Restore(
func(path string, mode fs.FileMode) (io.WriteCloser, error) {
return writer, nil
},
func(path string, mode fs.FileMode) error {
// Not necessary; can't read on a directory
return nil
},
f.path,
"",
true,
); err != nil {
if err == io.ErrClosedPipe {
return
}
// TODO: Handle error
panic(err)
}
}()
f.readOpReader = reader
f.readOpWriter = writer
}
written, err := io.CopyN(io.Discard, f.readOpReader, dst-int64(f.readOpReader.BytesRead))
if err == io.EOF {
return written, io.EOF
}
if err != nil {
return -1, err
}
return written, nil
} }
// Write operations // Write operations
@@ -526,7 +532,11 @@ func (f *File) WriteAt(p []byte, off int64) (n int, err error) {
return -1, err return -1, err
} }
return -1, ErrNotImplemented if _, err := f.seekWithoutLocking(off, io.SeekStart); err != nil {
return -1, err
}
return f.writeBuf.Write(p)
} }
func (f *File) WriteString(s string) (ret int, err error) { func (f *File) WriteString(s string) (ret int, err error) {