github.com/bpfs/defs@v0.0.15/afero/gcsfs/file_info.go (about) 1 // Copyright © 2021 Vasily Ovchinnikov <vasily@remerge.io>. 2 // 3 // The code in this file is derived from afero fork github.com/Zatte/afero by Mikael Rapp 4 // licensed under Apache License 2.0. 5 // 6 // Licensed under the Apache License, Version 2.0 (the "License"); 7 // you may not use this file except in compliance with the License. 8 // You may obtain a copy of the License at 9 // http://www.apache.org/licenses/LICENSE-2.0 10 // 11 // Unless required by applicable law or agreed to in writing, software 12 // distributed under the License is distributed on an "AS IS" BASIS, 13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 // See the License for the specific language governing permissions and 15 // limitations under the License. 16 17 package gcsfs 18 19 import ( 20 "os" 21 "path/filepath" 22 "strings" 23 "time" 24 25 "cloud.google.com/go/storage" 26 ) 27 28 const ( 29 folderSize = 42 30 ) 31 32 type FileInfo struct { 33 name string 34 size int64 35 updated time.Time 36 isDir bool 37 fileMode os.FileMode 38 } 39 40 func newFileInfo(name string, fs *Fs, fileMode os.FileMode) (*FileInfo, error) { 41 res := &FileInfo{ 42 name: name, 43 size: folderSize, 44 updated: time.Time{}, 45 isDir: false, 46 fileMode: fileMode, 47 } 48 49 obj, err := fs.getObj(name) 50 if err != nil { 51 return nil, err 52 } 53 54 objAttrs, err := obj.Attrs(fs.ctx) 55 if err != nil { 56 if err.Error() == ErrEmptyObjectName.Error() { 57 // It's a root folder here, we return right away 58 res.name = fs.ensureTrailingSeparator(res.name) 59 res.isDir = true 60 return res, nil 61 } else if err.Error() == ErrObjectDoesNotExist.Error() { 62 // Folders do not actually "exist" in GCloud, so we have to check, if something exists with 63 // such a prefix 64 bucketName, bucketPath := fs.splitName(name) 65 it := fs.client.Bucket(bucketName).Objects( 66 fs.ctx, &storage.Query{Delimiter: fs.separator, Prefix: bucketPath, Versions: false}) 67 if _, err = it.Next(); err == nil { 68 res.name = fs.ensureTrailingSeparator(res.name) 69 res.isDir = true 70 return res, nil 71 } 72 73 return nil, ErrFileNotFound 74 } 75 return nil, err 76 } 77 78 res.size = objAttrs.Size 79 res.updated = objAttrs.Updated 80 81 return res, nil 82 } 83 84 func newFileInfoFromAttrs(objAttrs *storage.ObjectAttrs, fileMode os.FileMode) *FileInfo { 85 res := &FileInfo{ 86 name: objAttrs.Name, 87 size: objAttrs.Size, 88 updated: objAttrs.Updated, 89 isDir: false, 90 fileMode: fileMode, 91 } 92 93 if res.name == "" { 94 if objAttrs.Prefix != "" { 95 // It's a virtual folder! It does not have a name, but prefix - this is how GCS API 96 // deals with them at the moment 97 res.name = objAttrs.Prefix 98 res.size = folderSize 99 res.isDir = true 100 } 101 } 102 103 return res 104 } 105 106 func (fi *FileInfo) Name() string { 107 return filepath.Base(filepath.FromSlash(fi.name)) 108 } 109 110 func (fi *FileInfo) Size() int64 { 111 return fi.size 112 } 113 114 func (fi *FileInfo) Mode() os.FileMode { 115 if fi.IsDir() { 116 return os.ModeDir | fi.fileMode 117 } 118 return fi.fileMode 119 } 120 121 func (fi *FileInfo) ModTime() time.Time { 122 return fi.updated 123 } 124 125 func (fi *FileInfo) IsDir() bool { 126 return fi.isDir 127 } 128 129 func (fi *FileInfo) Sys() interface{} { 130 return nil 131 } 132 133 type ByName []*FileInfo 134 135 func (a ByName) Len() int { return len(a) } 136 func (a ByName) Swap(i, j int) { 137 a[i].name, a[j].name = a[j].name, a[i].name 138 a[i].size, a[j].size = a[j].size, a[i].size 139 a[i].updated, a[j].updated = a[j].updated, a[i].updated 140 a[i].isDir, a[j].isDir = a[j].isDir, a[i].isDir 141 } 142 func (a ByName) Less(i, j int) bool { return strings.Compare(a[i].Name(), a[j].Name()) == -1 }