in component/file_cache/file_cache.go [907:1052]
func (fc *FileCache) OpenFile(options internal.OpenFileOptions) (*handlemap.Handle, error) {
log.Trace("FileCache::OpenFile : name=%s, flags=%d, mode=%s", options.Name, options.Flags, options.Mode)
localPath := filepath.Join(fc.tmpPath, options.Name)
var f *os.File
var err error
flock := fc.fileLocks.Get(options.Name)
flock.Lock()
defer flock.Unlock()
fc.policy.CacheValid(localPath)
downloadRequired, fileExists, attr, err := fc.isDownloadRequired(localPath, options.Name, flock)
// return err in case of authorization permission mismatch
if err != nil && err == syscall.EACCES {
return nil, err
}
if downloadRequired {
log.Debug("FileCache::OpenFile : Need to re-download %s", options.Name)
fileSize := int64(0)
if attr != nil {
fileSize = int64(attr.Size)
}
if fileExists {
log.Debug("FileCache::OpenFile : Delete cached file %s", options.Name)
err := deleteFile(localPath)
if err != nil && !os.IsNotExist(err) {
log.Err("FileCache::OpenFile : Failed to delete old file %s", options.Name)
}
} else {
// Create the file if if doesn't already exist.
err := os.MkdirAll(filepath.Dir(localPath), fc.defaultPermission)
if err != nil {
log.Err("FileCache::OpenFile : error creating directory structure for file %s [%s]", options.Name, err.Error())
return nil, err
}
}
// Open the file in write mode.
f, err = os.OpenFile(localPath, os.O_CREATE|os.O_RDWR, options.Mode)
if err != nil {
log.Err("FileCache::OpenFile : error creating new file %s [%s]", options.Name, err.Error())
return nil, err
}
if options.Flags&os.O_TRUNC != 0 {
fileSize = 0
}
if fileSize > 0 {
if fc.diskHighWaterMark != 0 {
currSize, err := common.GetUsage(fc.tmpPath)
if err != nil {
log.Err("FileCache::OpenFile : error getting current usage of cache [%s]", err.Error())
} else {
if (currSize + float64(fileSize)) > fc.diskHighWaterMark {
log.Err("FileCache::OpenFile : cache size limit reached [%f] failed to open %s", fc.maxCacheSize, options.Name)
return nil, syscall.ENOSPC
}
}
}
// Download/Copy the file from storage to the local file.
err = fc.NextComponent().CopyToFile(
internal.CopyToFileOptions{
Name: options.Name,
Offset: 0,
Count: fileSize,
File: f,
})
if err != nil {
// File was created locally and now download has failed so we need to delete it back from local cache
log.Err("FileCache::OpenFile : error downloading file from storage %s [%s]", options.Name, err.Error())
_ = f.Close()
_ = os.Remove(localPath)
return nil, err
}
}
// Update the last download time of this file
flock.SetDownloadTime()
log.Debug("FileCache::OpenFile : Download of %s is complete", options.Name)
f.Close()
// After downloading the file, update the modified times and mode of the file.
fileMode := fc.defaultPermission
if attr != nil && !attr.IsModeDefault() {
fileMode = attr.Mode
}
// If user has selected some non default mode in config then every local file shall be created with that mode only
err = os.Chmod(localPath, fileMode)
if err != nil {
log.Err("FileCache::OpenFile : Failed to change mode of file %s [%s]", options.Name, err.Error())
}
// TODO: When chown is supported should we update that?
if attr != nil {
// chtimes shall be the last api otherwise calling chmod/chown will update the last change time
err = os.Chtimes(localPath, attr.Atime, attr.Mtime)
if err != nil {
log.Err("FileCache::OpenFile : Failed to change times of file %s [%s]", options.Name, err.Error())
}
}
fileCacheStatsCollector.UpdateStats(stats_manager.Increment, dlFiles, (int64)(1))
} else {
log.Debug("FileCache::OpenFile : %s will be served from cache", options.Name)
fileCacheStatsCollector.UpdateStats(stats_manager.Increment, cacheServed, (int64)(1))
}
// Open the file and grab a shared lock to prevent deletion by the cache policy.
f, err = os.OpenFile(localPath, options.Flags, options.Mode)
if err != nil {
log.Err("FileCache::OpenFile : error opening cached file %s [%s]", options.Name, err.Error())
return nil, err
}
// Increment the handle count in this lock item as there is one handle open for this now
flock.Inc()
handle := handlemap.NewHandle(options.Name)
if options.Flags&os.O_TRUNC != 0 {
handle.Flags.Set(handlemap.HandleFlagDirty)
}
inf, err := f.Stat()
if err == nil {
handle.Size = inf.Size()
}
handle.UnixFD = uint64(f.Fd())
if !fc.offloadIO {
handle.Flags.Set(handlemap.HandleFlagCached)
}
log.Info("FileCache::OpenFile : file=%s, fd=%d", options.Name, f.Fd())
handle.SetFileObject(f)
return handle, nil
}