github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/write.go (about) 1 package vfs 2 3 import ( 4 "context" 5 "io" 6 "os" 7 "sync" 8 "time" 9 10 "github.com/rclone/rclone/fs" 11 "github.com/rclone/rclone/fs/operations" 12 ) 13 14 // WriteFileHandle is an open for write handle on a File 15 type WriteFileHandle struct { 16 baseHandle 17 mu sync.Mutex 18 cond sync.Cond // cond lock for out of sequence writes 19 remote string 20 pipeWriter *io.PipeWriter 21 o fs.Object 22 result chan error 23 file *File 24 offset int64 25 flags int 26 closed bool // set if handle has been closed 27 writeCalled bool // set the first time Write() is called 28 opened bool 29 truncated bool 30 } 31 32 // Check interfaces 33 var ( 34 _ io.Writer = (*WriteFileHandle)(nil) 35 _ io.WriterAt = (*WriteFileHandle)(nil) 36 _ io.Closer = (*WriteFileHandle)(nil) 37 ) 38 39 func newWriteFileHandle(d *Dir, f *File, remote string, flags int) (*WriteFileHandle, error) { 40 fh := &WriteFileHandle{ 41 remote: remote, 42 flags: flags, 43 result: make(chan error, 1), 44 file: f, 45 } 46 fh.cond = sync.Cond{L: &fh.mu} 47 fh.file.addWriter(fh) 48 return fh, nil 49 } 50 51 // returns whether it is OK to truncate the file 52 func (fh *WriteFileHandle) safeToTruncate() bool { 53 return fh.truncated || fh.flags&os.O_TRUNC != 0 || !fh.file.exists() 54 } 55 56 // openPending opens the file if there is a pending open 57 // 58 // call with the lock held 59 func (fh *WriteFileHandle) openPending() (err error) { 60 if fh.opened { 61 return nil 62 } 63 if !fh.safeToTruncate() { 64 fs.Errorf(fh.remote, "WriteFileHandle: Can't open for write without O_TRUNC on existing file without --vfs-cache-mode >= writes") 65 return EPERM 66 } 67 var pipeReader *io.PipeReader 68 pipeReader, fh.pipeWriter = io.Pipe() 69 go func() { 70 // NB Rcat deals with Stats.Transferring, etc. 71 o, err := operations.Rcat(context.TODO(), fh.file.Fs(), fh.remote, pipeReader, time.Now(), nil) 72 if err != nil { 73 fs.Errorf(fh.remote, "WriteFileHandle.New Rcat failed: %v", err) 74 } 75 // Close the pipeReader so the pipeWriter fails with ErrClosedPipe 76 _ = pipeReader.Close() 77 fh.o = o 78 fh.result <- err 79 }() 80 fh.file.setSize(0) 81 fh.truncated = true 82 fh.file.Dir().addObject(fh.file) // make sure the directory has this object in it now 83 fh.opened = true 84 return nil 85 } 86 87 // String converts it to printable 88 func (fh *WriteFileHandle) String() string { 89 if fh == nil { 90 return "<nil *WriteFileHandle>" 91 } 92 fh.mu.Lock() 93 defer fh.mu.Unlock() 94 if fh.file == nil { 95 return "<nil *WriteFileHandle.file>" 96 } 97 return fh.file.String() + " (w)" 98 } 99 100 // Node returns the Node associated with this - satisfies Noder interface 101 func (fh *WriteFileHandle) Node() Node { 102 fh.mu.Lock() 103 defer fh.mu.Unlock() 104 return fh.file 105 } 106 107 // WriteAt writes len(p) bytes from p to the underlying data stream at offset 108 // off. It returns the number of bytes written from p (0 <= n <= len(p)) and 109 // any error encountered that caused the write to stop early. WriteAt must 110 // return a non-nil error if it returns n < len(p). 111 // 112 // If WriteAt is writing to a destination with a seek offset, WriteAt should 113 // not affect nor be affected by the underlying seek offset. 114 // 115 // Clients of WriteAt can execute parallel WriteAt calls on the same 116 // destination if the ranges do not overlap. 117 // 118 // Implementations must not retain p. 119 func (fh *WriteFileHandle) WriteAt(p []byte, off int64) (n int, err error) { 120 fh.mu.Lock() 121 defer fh.mu.Unlock() 122 return fh.writeAt(p, off) 123 } 124 125 // Implementation of WriteAt - call with lock held 126 func (fh *WriteFileHandle) writeAt(p []byte, off int64) (n int, err error) { 127 // defer log.Trace(fh.remote, "len=%d off=%d", len(p), off)("n=%d, fh.off=%d, err=%v", &n, &fh.offset, &err) 128 if fh.closed { 129 fs.Errorf(fh.remote, "WriteFileHandle.Write: error: %v", EBADF) 130 return 0, ECLOSED 131 } 132 if fh.offset != off { 133 waitSequential("write", fh.remote, &fh.cond, fh.file.VFS().Opt.WriteWait, &fh.offset, off) 134 } 135 if fh.offset != off { 136 fs.Errorf(fh.remote, "WriteFileHandle.Write: can't seek in file without --vfs-cache-mode >= writes") 137 return 0, ESPIPE 138 } 139 if err = fh.openPending(); err != nil { 140 return 0, err 141 } 142 fh.writeCalled = true 143 n, err = fh.pipeWriter.Write(p) 144 fh.offset += int64(n) 145 fh.file.setSize(fh.offset) 146 if err != nil { 147 fs.Errorf(fh.remote, "WriteFileHandle.Write error: %v", err) 148 return 0, err 149 } 150 // fs.Debugf(fh.remote, "WriteFileHandle.Write OK (%d bytes written)", n) 151 fh.cond.Broadcast() // wake everyone up waiting for an in-sequence read 152 return n, nil 153 } 154 155 // Write writes len(p) bytes from p to the underlying data stream. It returns 156 // the number of bytes written from p (0 <= n <= len(p)) and any error 157 // encountered that caused the write to stop early. Write must return a non-nil 158 // error if it returns n < len(p). Write must not modify the slice data, even 159 // temporarily. 160 // 161 // Implementations must not retain p. 162 func (fh *WriteFileHandle) Write(p []byte) (n int, err error) { 163 fh.mu.Lock() 164 defer fh.mu.Unlock() 165 // Since we can't seek, just call WriteAt with the current offset 166 return fh.writeAt(p, fh.offset) 167 } 168 169 // WriteString a string to the file 170 func (fh *WriteFileHandle) WriteString(s string) (n int, err error) { 171 return fh.Write([]byte(s)) 172 } 173 174 // Offset returns the offset of the file pointer 175 func (fh *WriteFileHandle) Offset() (offset int64) { 176 fh.mu.Lock() 177 defer fh.mu.Unlock() 178 return fh.offset 179 } 180 181 // close the file handle returning EBADF if it has been 182 // closed already. 183 // 184 // Must be called with fh.mu held 185 func (fh *WriteFileHandle) close() (err error) { 186 if fh.closed { 187 return ECLOSED 188 } 189 fh.closed = true 190 // leave writer open until file is transferred 191 defer func() { 192 fh.file.delWriter(fh) 193 }() 194 // If file not opened and not safe to truncate then leave file intact 195 if !fh.opened && !fh.safeToTruncate() { 196 return nil 197 } 198 if err = fh.openPending(); err != nil { 199 return err 200 } 201 writeCloseErr := fh.pipeWriter.Close() 202 err = <-fh.result 203 if err == nil { 204 fh.file.setObject(fh.o) 205 err = writeCloseErr 206 } else { 207 // Remove vfs file entry when no object is present 208 if fh.file.getObject() == nil { 209 _ = fh.file.Remove() 210 } 211 } 212 return err 213 } 214 215 // Close closes the file 216 func (fh *WriteFileHandle) Close() error { 217 fh.mu.Lock() 218 defer fh.mu.Unlock() 219 return fh.close() 220 } 221 222 // Flush is called on each close() of a file descriptor. So if a 223 // filesystem wants to return write errors in close() and the file has 224 // cached dirty data, this is a good place to write back data and 225 // return any errors. Since many applications ignore close() errors 226 // this is not always useful. 227 // 228 // NOTE: The flush() method may be called more than once for each 229 // open(). This happens if more than one file descriptor refers to an 230 // opened file due to dup(), dup2() or fork() calls. It is not 231 // possible to determine if a flush is final, so each flush should be 232 // treated equally. Multiple write-flush sequences are relatively 233 // rare, so this shouldn't be a problem. 234 // 235 // Filesystems shouldn't assume that flush will always be called after 236 // some writes, or that if will be called at all. 237 func (fh *WriteFileHandle) Flush() error { 238 fh.mu.Lock() 239 defer fh.mu.Unlock() 240 if fh.closed { 241 fs.Debugf(fh.remote, "WriteFileHandle.Flush nothing to do") 242 return nil 243 } 244 // fs.Debugf(fh.remote, "WriteFileHandle.Flush") 245 // If Write hasn't been called then ignore the Flush - Release 246 // will pick it up 247 if !fh.writeCalled { 248 fs.Debugf(fh.remote, "WriteFileHandle.Flush unwritten handle, writing 0 bytes to avoid race conditions") 249 _, err := fh.writeAt([]byte{}, fh.offset) 250 return err 251 } 252 err := fh.close() 253 if err != nil { 254 fs.Errorf(fh.remote, "WriteFileHandle.Flush error: %v", err) 255 //} else { 256 // fs.Debugf(fh.remote, "WriteFileHandle.Flush OK") 257 } 258 return err 259 } 260 261 // Release is called when we are finished with the file handle 262 // 263 // It isn't called directly from userspace so the error is ignored by 264 // the kernel 265 func (fh *WriteFileHandle) Release() error { 266 fh.mu.Lock() 267 defer fh.mu.Unlock() 268 if fh.closed { 269 fs.Debugf(fh.remote, "WriteFileHandle.Release nothing to do") 270 return nil 271 } 272 fs.Debugf(fh.remote, "WriteFileHandle.Release closing") 273 err := fh.close() 274 if err != nil { 275 fs.Errorf(fh.remote, "WriteFileHandle.Release error: %v", err) 276 //} else { 277 // fs.Debugf(fh.remote, "WriteFileHandle.Release OK") 278 } 279 return err 280 } 281 282 // Stat returns info about the file 283 func (fh *WriteFileHandle) Stat() (os.FileInfo, error) { 284 fh.mu.Lock() 285 defer fh.mu.Unlock() 286 return fh.file, nil 287 } 288 289 // Truncate file to given size 290 func (fh *WriteFileHandle) Truncate(size int64) (err error) { 291 // defer log.Trace(fh.remote, "size=%d", size)("err=%v", &err) 292 fh.mu.Lock() 293 defer fh.mu.Unlock() 294 if size != fh.offset { 295 fs.Errorf(fh.remote, "WriteFileHandle: Truncate: Can't change size without --vfs-cache-mode >= writes") 296 return EPERM 297 } 298 // File is correct size 299 if size == 0 { 300 fh.truncated = true 301 } 302 return nil 303 } 304 305 // Read reads up to len(p) bytes into p. 306 func (fh *WriteFileHandle) Read(p []byte) (n int, err error) { 307 fs.Errorf(fh.remote, "WriteFileHandle: Read: Can't read and write to file without --vfs-cache-mode >= minimal") 308 return 0, EPERM 309 } 310 311 // ReadAt reads len(p) bytes into p starting at offset off in the 312 // underlying input source. It returns the number of bytes read (0 <= 313 // n <= len(p)) and any error encountered. 314 func (fh *WriteFileHandle) ReadAt(p []byte, off int64) (n int, err error) { 315 fs.Errorf(fh.remote, "WriteFileHandle: ReadAt: Can't read and write to file without --vfs-cache-mode >= minimal") 316 return 0, EPERM 317 } 318 319 // Sync commits the current contents of the file to stable storage. Typically, 320 // this means flushing the file system's in-memory copy of recently written 321 // data to disk. 322 func (fh *WriteFileHandle) Sync() error { 323 return nil 324 } 325 326 // Name returns the name of the file from the underlying Object. 327 func (fh *WriteFileHandle) Name() string { 328 return fh.file.String() 329 }