github.com/rclone/rclone@v1.66.1-0.20240517100346-7b89735ae726/vfs/read_write.go (about) 1 package vfs 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "sync" 8 9 "github.com/rclone/rclone/fs" 10 "github.com/rclone/rclone/fs/log" 11 "github.com/rclone/rclone/vfs/vfscache" 12 ) 13 14 // RWFileHandle is a handle that can be open for read and write. 15 // 16 // It will be open to a temporary file which, when closed, will be 17 // transferred to the remote. 18 type RWFileHandle struct { 19 // read only variables 20 file *File 21 d *Dir 22 flags int // open flags 23 item *vfscache.Item // cached file item 24 25 // read write variables protected by mutex 26 mu sync.Mutex 27 offset int64 // file pointer offset 28 closed bool // set if handle has been closed 29 opened bool 30 writeCalled bool // if any Write() methods have been called 31 } 32 33 // Lock performs Unix locking, not supported 34 func (fh *RWFileHandle) Lock() error { 35 return os.ErrInvalid 36 } 37 38 // Unlock performs Unix unlocking, not supported 39 func (fh *RWFileHandle) Unlock() error { 40 return os.ErrInvalid 41 } 42 43 func newRWFileHandle(d *Dir, f *File, flags int) (fh *RWFileHandle, err error) { 44 defer log.Trace(f.Path(), "")("err=%v", &err) 45 // get an item to represent this from the cache 46 item := d.vfs.cache.Item(f.Path()) 47 48 exists := f.exists() || (item.Exists() && !item.WrittenBack()) 49 50 // if O_CREATE and O_EXCL are set and if path already exists, then return EEXIST 51 if flags&(os.O_CREATE|os.O_EXCL) == os.O_CREATE|os.O_EXCL && exists { 52 return nil, EEXIST 53 } 54 55 fh = &RWFileHandle{ 56 file: f, 57 d: d, 58 flags: flags, 59 item: item, 60 } 61 62 // truncate immediately if O_TRUNC is set or O_CREATE is set and file doesn't exist 63 if !fh.readOnly() && (fh.flags&os.O_TRUNC != 0 || (fh.flags&os.O_CREATE != 0 && !exists)) { 64 err = fh.Truncate(0) 65 if err != nil { 66 return nil, fmt.Errorf("cache open with O_TRUNC: failed to truncate: %w", err) 67 } 68 // we definitely need to write back the item even if we don't write to it 69 item.Dirty() 70 } 71 72 if !fh.readOnly() { 73 fh.file.addWriter(fh) 74 } 75 76 return fh, nil 77 } 78 79 // readOnly returns whether flags say fh is read only 80 func (fh *RWFileHandle) readOnly() bool { 81 return (fh.flags & accessModeMask) == os.O_RDONLY 82 } 83 84 // writeOnly returns whether flags say fh is write only 85 func (fh *RWFileHandle) writeOnly() bool { 86 return (fh.flags & accessModeMask) == os.O_WRONLY 87 } 88 89 // openPending opens the file if there is a pending open 90 // 91 // call with the lock held 92 func (fh *RWFileHandle) openPending() (err error) { 93 if fh.opened { 94 return nil 95 } 96 defer log.Trace(fh.logPrefix(), "")("err=%v", &err) 97 98 fh.file.muRW.Lock() 99 defer fh.file.muRW.Unlock() 100 101 o := fh.file.getObject() 102 err = fh.item.Open(o) 103 if err != nil { 104 return fmt.Errorf("open RW handle failed to open cache file: %w", err) 105 } 106 107 size := fh._size() // update size in file and read size 108 if fh.flags&os.O_APPEND != 0 { 109 fh.offset = size 110 fs.Debugf(fh.logPrefix(), "open at offset %d", fh.offset) 111 } else { 112 fh.offset = 0 113 } 114 fh.opened = true 115 fh.d.addObject(fh.file) // make sure the directory has this object in it now 116 return nil 117 } 118 119 // String converts it to printable 120 func (fh *RWFileHandle) String() string { 121 if fh == nil { 122 return "<nil *RWFileHandle>" 123 } 124 if fh.file == nil { 125 return "<nil *RWFileHandle.file>" 126 } 127 return fh.file.String() + " (rw)" 128 } 129 130 // Node returns the Node associated with this - satisfies Noder interface 131 func (fh *RWFileHandle) Node() Node { 132 fh.mu.Lock() 133 defer fh.mu.Unlock() 134 return fh.file 135 } 136 137 // updateSize updates the size of the file if necessary 138 // 139 // Must be called with fh.mu held 140 func (fh *RWFileHandle) updateSize() { 141 // If read only or not opened then ignore 142 if fh.readOnly() || !fh.opened { 143 return 144 } 145 size := fh._size() 146 fh.file.setSize(size) 147 } 148 149 // close the file handle returning EBADF if it has been 150 // closed already. 151 // 152 // Must be called with fh.mu held. 153 // 154 // Note that we leave the file around in the cache on error conditions 155 // to give the user a chance to recover it. 156 func (fh *RWFileHandle) close() (err error) { 157 defer log.Trace(fh.logPrefix(), "")("err=%v", &err) 158 fh.file.muRW.Lock() 159 defer fh.file.muRW.Unlock() 160 161 if fh.closed { 162 return ECLOSED 163 } 164 165 fh.closed = true 166 fh.updateSize() 167 if fh.opened { 168 err = fh.item.Close(fh.file.setObject) 169 fh.opened = false 170 } else { 171 // apply any pending mod times if any 172 _ = fh.file.applyPendingModTime() 173 } 174 175 if !fh.readOnly() { 176 fh.file.delWriter(fh) 177 } 178 179 return err 180 } 181 182 // Close closes the file 183 func (fh *RWFileHandle) Close() error { 184 fh.mu.Lock() 185 defer fh.mu.Unlock() 186 return fh.close() 187 } 188 189 // Flush is called each time the file or directory is closed. 190 // Because there can be multiple file descriptors referring to a 191 // single opened file, Flush can be called multiple times. 192 func (fh *RWFileHandle) Flush() error { 193 fh.mu.Lock() 194 fs.Debugf(fh.logPrefix(), "RWFileHandle.Flush") 195 fh.updateSize() 196 fh.mu.Unlock() 197 return nil 198 } 199 200 // Release is called when we are finished with the file handle 201 // 202 // It isn't called directly from userspace so the error is ignored by 203 // the kernel 204 func (fh *RWFileHandle) Release() error { 205 fh.mu.Lock() 206 defer fh.mu.Unlock() 207 fs.Debugf(fh.logPrefix(), "RWFileHandle.Release") 208 if fh.closed { 209 // Don't return an error if called twice 210 return nil 211 } 212 err := fh.close() 213 if err != nil { 214 fs.Errorf(fh.logPrefix(), "RWFileHandle.Release error: %v", err) 215 } 216 return err 217 } 218 219 // _size returns the size of the underlying file and also sets it in 220 // the owning file 221 // 222 // call with the lock held 223 func (fh *RWFileHandle) _size() int64 { 224 size, err := fh.item.GetSize() 225 if err != nil { 226 o := fh.file.getObject() 227 if o != nil { 228 size = o.Size() 229 } else { 230 fs.Errorf(fh.logPrefix(), "Couldn't read size of file") 231 size = 0 232 } 233 } 234 fh.file.setSize(size) 235 return size 236 } 237 238 // Size returns the size of the underlying file 239 func (fh *RWFileHandle) Size() int64 { 240 fh.mu.Lock() 241 defer fh.mu.Unlock() 242 return fh._size() 243 } 244 245 // Stat returns info about the file 246 func (fh *RWFileHandle) Stat() (os.FileInfo, error) { 247 fh.mu.Lock() 248 defer fh.mu.Unlock() 249 return fh.file, nil 250 } 251 252 // _readAt bytes from the file at off 253 // 254 // if release is set then it releases the mutex just before doing the IO 255 // 256 // call with lock held 257 func (fh *RWFileHandle) _readAt(b []byte, off int64, release bool) (n int, err error) { 258 defer log.Trace(fh.logPrefix(), "size=%d, off=%d", len(b), off)("n=%d, err=%v", &n, &err) 259 if fh.closed { 260 return n, ECLOSED 261 } 262 if fh.writeOnly() { 263 return n, EBADF 264 } 265 if off >= fh._size() { 266 return n, io.EOF 267 } 268 if err = fh.openPending(); err != nil { 269 return n, err 270 } 271 if release { 272 // Do the writing with fh.mu unlocked 273 fh.mu.Unlock() 274 } 275 276 n, err = fh.item.ReadAt(b, off) 277 278 if release { 279 fh.mu.Lock() 280 } 281 return n, err 282 } 283 284 // ReadAt bytes from the file at off 285 func (fh *RWFileHandle) ReadAt(b []byte, off int64) (n int, err error) { 286 fh.mu.Lock() 287 defer fh.mu.Unlock() 288 return fh._readAt(b, off, true) 289 } 290 291 // Read bytes from the file 292 func (fh *RWFileHandle) Read(b []byte) (n int, err error) { 293 fh.mu.Lock() 294 defer fh.mu.Unlock() 295 n, err = fh._readAt(b, fh.offset, false) 296 fh.offset += int64(n) 297 return n, err 298 } 299 300 // Seek to new file position 301 func (fh *RWFileHandle) Seek(offset int64, whence int) (ret int64, err error) { 302 fh.mu.Lock() 303 defer fh.mu.Unlock() 304 if fh.closed { 305 return 0, ECLOSED 306 } 307 if !fh.opened && offset == 0 && whence != 2 { 308 return 0, nil 309 } 310 if err = fh.openPending(); err != nil { 311 return ret, err 312 } 313 switch whence { 314 case io.SeekStart: 315 fh.offset = 0 316 case io.SeekEnd: 317 fh.offset = fh._size() 318 } 319 fh.offset += offset 320 // we don't check the offset - the next Read will 321 return fh.offset, nil 322 } 323 324 // _writeAt bytes to the file at off 325 // 326 // if release is set then it releases the mutex just before doing the IO 327 // 328 // call with lock held 329 func (fh *RWFileHandle) _writeAt(b []byte, off int64, release bool) (n int, err error) { 330 defer log.Trace(fh.logPrefix(), "size=%d, off=%d", len(b), off)("n=%d, err=%v", &n, &err) 331 if fh.closed { 332 return n, ECLOSED 333 } 334 if fh.readOnly() { 335 return n, EBADF 336 } 337 if err = fh.openPending(); err != nil { 338 return n, err 339 } 340 if fh.flags&os.O_APPEND != 0 { 341 // From open(2): Before each write(2), the file offset is 342 // positioned at the end of the file, as if with lseek(2). 343 size := fh._size() 344 fh.offset = size 345 off = fh.offset 346 } 347 fh.writeCalled = true 348 if release { 349 // Do the writing with fh.mu unlocked 350 fh.mu.Unlock() 351 } 352 n, err = fh.item.WriteAt(b, off) 353 if release { 354 fh.mu.Lock() 355 } 356 if err != nil { 357 return n, err 358 } 359 360 _ = fh._size() 361 return n, err 362 } 363 364 // WriteAt bytes to the file at off 365 func (fh *RWFileHandle) WriteAt(b []byte, off int64) (n int, err error) { 366 fh.mu.Lock() 367 n, err = fh._writeAt(b, off, true) 368 if fh.flags&os.O_APPEND != 0 { 369 fh.offset += int64(n) 370 } 371 fh.mu.Unlock() 372 return n, err 373 } 374 375 // Write bytes to the file 376 func (fh *RWFileHandle) Write(b []byte) (n int, err error) { 377 fh.mu.Lock() 378 defer fh.mu.Unlock() 379 n, err = fh._writeAt(b, fh.offset, false) 380 fh.offset += int64(n) 381 return n, err 382 } 383 384 // WriteString a string to the file 385 func (fh *RWFileHandle) WriteString(s string) (n int, err error) { 386 return fh.Write([]byte(s)) 387 } 388 389 // Truncate file to given size 390 // 391 // Call with mutex held 392 func (fh *RWFileHandle) _truncate(size int64) (err error) { 393 if size == fh._size() { 394 return nil 395 } 396 fh.file.setSize(size) 397 return fh.item.Truncate(size) 398 } 399 400 // Truncate file to given size 401 func (fh *RWFileHandle) Truncate(size int64) (err error) { 402 fh.mu.Lock() 403 defer fh.mu.Unlock() 404 if fh.closed { 405 return ECLOSED 406 } 407 if err = fh.openPending(); err != nil { 408 return err 409 } 410 return fh._truncate(size) 411 } 412 413 // Sync commits the current contents of the file to stable storage. Typically, 414 // this means flushing the file system's in-memory copy of recently written 415 // data to disk. 416 func (fh *RWFileHandle) Sync() error { 417 fh.mu.Lock() 418 defer fh.mu.Unlock() 419 if fh.closed { 420 return ECLOSED 421 } 422 if !fh.opened { 423 return nil 424 } 425 if fh.readOnly() { 426 return nil 427 } 428 return fh.item.Sync() 429 } 430 431 func (fh *RWFileHandle) logPrefix() string { 432 return fmt.Sprintf("%s(%p)", fh.file.Path(), fh) 433 } 434 435 // Chdir changes the current working directory to the file, which must 436 // be a directory. 437 func (fh *RWFileHandle) Chdir() error { 438 return ENOSYS 439 } 440 441 // Chmod changes the mode of the file to mode. 442 func (fh *RWFileHandle) Chmod(mode os.FileMode) error { 443 return ENOSYS 444 } 445 446 // Chown changes the numeric uid and gid of the named file. 447 func (fh *RWFileHandle) Chown(uid, gid int) error { 448 return ENOSYS 449 } 450 451 // Fd returns the integer Unix file descriptor referencing the open file. 452 func (fh *RWFileHandle) Fd() uintptr { 453 return 0xdeadbeef // FIXME 454 } 455 456 // Name returns the name of the file from the underlying Object. 457 func (fh *RWFileHandle) Name() string { 458 return fh.file.String() 459 } 460 461 // Readdir reads the contents of the directory associated with file. 462 func (fh *RWFileHandle) Readdir(n int) ([]os.FileInfo, error) { 463 return nil, ENOSYS 464 } 465 466 // Readdirnames reads the contents of the directory associated with file. 467 func (fh *RWFileHandle) Readdirnames(n int) (names []string, err error) { 468 return nil, ENOSYS 469 }