func()

in component/file_cache/file_cache.go [1258:1366]


func (fc *FileCache) FlushFile(options internal.FlushFileOptions) error {
	//defer exectime.StatTimeCurrentBlock("FileCache::FlushFile")()
	log.Trace("FileCache::FlushFile : handle=%d, path=%s", options.Handle.ID, options.Handle.Path)

	// The file should already be in the cache since CreateFile/OpenFile was called before and a shared lock was acquired.
	localPath := filepath.Join(fc.tmpPath, options.Handle.Path)
	fc.policy.CacheValid(localPath)
	// if our handle is dirty then that means we wrote to the file
	if options.Handle.Dirty() {
		if fc.lazyWrite && !options.CloseInProgress {
			// As lazy-write is enable, upload will be scheduled when file is closed.
			log.Info("FileCache::FlushFile : %s will be flushed when handle %d is closed", options.Handle.Path, options.Handle.ID)
			return nil
		}

		f := options.Handle.GetFileObject()
		if f == nil {
			log.Err("FileCache::FlushFile : error [couldn't find fd in handle] %s", options.Handle.Path)
			return syscall.EBADF
		}

		// Flush all data to disk that has been buffered by the kernel.
		// We cannot close the incoming handle since the user called flush, note close and flush can be called on the same handle multiple times.
		// To ensure the data is flushed to disk before writing to storage, we duplicate the handle and close that handle.
		// f.fsync() is another option but dup+close does it quickly compared to sync
		dupFd, err := syscall.Dup(int(f.Fd()))
		if err != nil {
			log.Err("FileCache::FlushFile : error [couldn't duplicate the fd] %s", options.Handle.Path)
			return syscall.EIO
		}

		err = syscall.Close(dupFd)
		if err != nil {
			log.Err("FileCache::FlushFile : error [unable to close duplicate fd] %s", options.Handle.Path)
			return syscall.EIO
		}

		// Write to storage
		// Create a new handle for the SDK to use to upload (read local file)
		// The local handle can still be used for read and write.
		var orgMode fs.FileMode
		modeChanged := false

		uploadHandle, err := os.Open(localPath)
		if err != nil {
			if os.IsPermission(err) {
				info, _ := os.Stat(localPath)
				orgMode = info.Mode()
				newMode := orgMode | 0444
				err = os.Chmod(localPath, newMode)
				if err == nil {
					modeChanged = true
					uploadHandle, err = os.Open(localPath)
					log.Info("FileCache::FlushFile : read mode added to file %s", options.Handle.Path)
				}
			}

			if err != nil {
				log.Err("FileCache::FlushFile : error [unable to open upload handle] %s [%s]", options.Handle.Path, err.Error())
				return err
			}
		}
		err = fc.NextComponent().CopyFromFile(
			internal.CopyFromFileOptions{
				Name: options.Handle.Path,
				File: uploadHandle,
			})

		uploadHandle.Close()

		if modeChanged {
			err1 := os.Chmod(localPath, orgMode)
			if err1 != nil {
				log.Err("FileCache::FlushFile : Failed to remove read mode from file %s [%s]", options.Handle.Path, err1.Error())
			}
		}

		if err != nil {
			log.Err("FileCache::FlushFile : %s upload failed [%s]", options.Handle.Path, err.Error())
			return err
		}

		options.Handle.Flags.Clear(handlemap.HandleFlagDirty)

		// If chmod was done on the file before it was uploaded to container then setting up mode would have been missed
		// Such file names are added to this map and here post upload we try to set the mode correctly
		_, found := fc.missedChmodList.Load(options.Handle.Path)
		if found {
			// If file is found in map it means last chmod was missed on this
			// Delete the entry from map so that any further flush do not try to update the mode again
			fc.missedChmodList.Delete(options.Handle.Path)

			// When chmod on container was missed, local file was updated with correct mode
			// Here take the mode from local cache and update the container accordingly
			localPath := filepath.Join(fc.tmpPath, options.Handle.Path)
			info, err := os.Lstat(localPath)
			if err == nil {
				err = fc.Chmod(internal.ChmodOptions{Name: options.Handle.Path, Mode: info.Mode()})
				if err != nil {
					// chmod was missed earlier for this file and doing it now also
					// resulted in error so ignore this one and proceed for flush handling
					log.Err("FileCache::FlushFile : %s chmod failed [%s]", options.Handle.Path, err.Error())
				}
			}
		}
	}

	return nil
}