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
}