feat: Implement File.WriteAt
This commit is contained in:
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user