@@ -48,7 +48,10 @@ func SetDebug(dbgFlags int) {
48
48
// -----------------------------------------------------------------------------------------
49
49
50
50
const (
51
- SysFilePrefix = ".fscache."
51
+ SysFilePrefix = ".fscache."
52
+
53
+ // readDir from local is ready if there is a dirListCacheFile
54
+ // see MarkDirCached
52
55
dirListCacheFile = SysFilePrefix + "ls"
53
56
)
54
57
@@ -61,22 +64,87 @@ func checkDirCached(dir string) fs.FileInfo {
61
64
return fi
62
65
}
63
66
64
- func TouchDirCached (dir string ) error {
67
+ // MarkDirCached marks the directory has dirList cache.
68
+ func MarkDirCached (dir string ) error {
65
69
cacheFile := filepath .Join (dir , dirListCacheFile )
66
70
return os .WriteFile (cacheFile , nil , 0666 )
67
71
}
68
72
69
- func WriteStubFile (localFile string , fi fs.FileInfo ) error {
73
+ // WriteStubFile writes a stub file to the local file system.
74
+ // If the file is a directory, it creates corresponding directory.
75
+ func WriteStubFile (localFile string , fi fs.FileInfo , udata uint64 ) error {
70
76
if fi .IsDir () {
77
+ // directory don't need to save FileInfo
71
78
return os .Mkdir (localFile , 0755 )
72
79
}
73
80
fi = & fileInfoRemote {fi }
74
- b := xdir .BytesFileInfo (fi )
81
+ b := xdir .BytesFileInfo (fi , udata )
75
82
dest := base64 .URLEncoding .EncodeToString (b )
76
83
// don't need to restore mtime for symlink (saved by BytesFileInfo)
77
84
return os .Symlink (dest , localFile )
78
85
}
79
86
87
+ // Udata returns the user data from the FileInfo.
88
+ func Udata (fi fs.FileInfo ) uint64 {
89
+ if fi , ok := fi .(interface { Udata () uint64 }); ok {
90
+ return fi .Udata ()
91
+ }
92
+ return 0
93
+ }
94
+
95
+ // MkStubFile creates a stub file with the specified name, size and mtime.
96
+ func MkStubFile (rootDir string , name string , size int64 , mtime time.Time , udata uint64 ) (err error ) {
97
+ file := filepath .Join (rootDir , name )
98
+ dir , fname := filepath .Split (file )
99
+ err = os .MkdirAll (dir , 0755 )
100
+ if err != nil {
101
+ return
102
+ }
103
+ fi := xfs .NewFileInfo (fname , size )
104
+ fi .Mtime = mtime
105
+ return WriteStubFile (file , fi , udata )
106
+ }
107
+
108
+ // ReaddirAll reads all entries in the directory and returns their cached FileInfo.
109
+ func ReaddirAll (localDir string , dir * os.File , offline bool ) (fis []fs.FileInfo , err error ) {
110
+ if fis , err = dir .Readdir (- 1 ); err != nil {
111
+ return
112
+ }
113
+ n := 0
114
+ for _ , fi := range fis {
115
+ name := fi .Name ()
116
+ if strings .HasPrefix (name , SysFilePrefix ) { // skip fscache system files
117
+ continue
118
+ }
119
+ if isRemote (fi ) {
120
+ if offline {
121
+ continue
122
+ }
123
+ localFile := filepath .Join (localDir , name )
124
+ fi = readStubFile (localFile , fi )
125
+ }
126
+ fis [n ] = fi
127
+ n ++
128
+ }
129
+ return fis [:n ], nil
130
+ }
131
+
132
+ // Lstat returns the FileInfo for the specified file or directory.
133
+ func Lstat (localFile string ) (fi fs.FileInfo , err error ) {
134
+ fi , err = os .Lstat (localFile )
135
+ if err != nil {
136
+ return
137
+ }
138
+ if fi .IsDir () {
139
+ if checkDirCached (localFile ) == nil { // no dir cache
140
+ fi = & fileInfoRemote {fi }
141
+ }
142
+ } else if isRemote (fi ) {
143
+ fi = readStubFile (localFile , fi )
144
+ }
145
+ return
146
+ }
147
+
80
148
func readStubFile (localFile string , fi fs.FileInfo ) fs.FileInfo {
81
149
dest , e1 := os .Readlink (localFile )
82
150
if e1 == nil {
@@ -109,6 +177,7 @@ func readdir(f http.File) ([]fs.FileInfo, error) {
109
177
110
178
// -----------------------------------------------------------------------------------------
111
179
180
+ /* TODO(xsw): cache file
112
181
type objFile struct {
113
182
http.File
114
183
localFile string
@@ -129,6 +198,7 @@ func (p *objFile) Close() error {
129
198
}
130
199
return file.Close()
131
200
}
201
+ */
132
202
133
203
type fileInfoRemote struct {
134
204
fs.FileInfo
@@ -147,48 +217,18 @@ type remote struct {
147
217
}
148
218
149
219
func (p * remote ) ReaddirAll (localDir string , dir * os.File , offline bool ) (fis []fs.FileInfo , err error ) {
150
- if fis , err = dir .Readdir (- 1 ); err != nil {
151
- return
152
- }
153
- n := 0
154
- for _ , fi := range fis {
155
- name := fi .Name ()
156
- if strings .HasPrefix (name , SysFilePrefix ) { // skip fscache system files
157
- continue
158
- }
159
- if isRemote (fi ) {
160
- if offline {
161
- continue
162
- }
163
- localFile := filepath .Join (localDir , name )
164
- fi = readStubFile (localFile , fi )
165
- }
166
- fis [n ] = fi
167
- n ++
168
- }
169
- return fis [:n ], nil
220
+ return ReaddirAll (localDir , dir , offline )
170
221
}
171
222
172
223
func (p * remote ) Lstat (localFile string ) (fi fs.FileInfo , err error ) {
173
- fi , err = os .Lstat (localFile )
174
- if err != nil {
175
- return
176
- }
177
- if fi .IsDir () {
178
- if checkDirCached (localFile ) == nil { // no dir cache
179
- fi = & fileInfoRemote {fi }
180
- }
181
- } else if isRemote (fi ) {
182
- fi = readStubFile (localFile , fi )
183
- }
184
- return
224
+ return Lstat (localFile )
185
225
}
186
226
187
227
func (p * remote ) SyncLstat (local string , name string ) (fi fs.FileInfo , err error ) {
188
228
return nil , os .ErrNotExist
189
229
}
190
230
191
- func (p * remote ) SyncOpen (local string , name string ) (f http.File , err error ) {
231
+ func (p * remote ) SyncOpen (local string , name string , fi fs. FileInfo ) (f http.File , err error ) {
192
232
f , err = p .bucket .Open (name )
193
233
if err != nil {
194
234
log .Printf (`[ERROR] bucket.Open("%s"): %v\n` , name , err )
@@ -197,7 +237,7 @@ func (p *remote) SyncOpen(local string, name string) (f http.File, err error) {
197
237
if debugNet {
198
238
log .Println ("[INFO] ==> bucket.Open" , name )
199
239
}
200
- if f .( interface { IsDir () bool }) .IsDir () {
240
+ if fi .IsDir () {
201
241
fis , e := readdir (f )
202
242
if e != nil {
203
243
log .Printf (`[ERROR] Readdir("%s"): %v\n` , name , e )
@@ -211,26 +251,29 @@ func (p *remote) SyncOpen(local string, name string) (f http.File, err error) {
211
251
base := filepath .Join (local , name )
212
252
for _ , fi := range fis {
213
253
itemFile := base + "/" + fi .Name ()
214
- if WriteStubFile (itemFile , fi ) != nil {
254
+ if WriteStubFile (itemFile , fi , 0 ) != nil {
215
255
nError ++
216
256
}
217
257
}
218
258
if nError == 0 {
219
- TouchDirCached (base )
259
+ MarkDirCached (base )
220
260
} else {
221
261
log .Printf ("[WARN] writeStubFile fail (%d errors)" , nError )
222
262
}
223
263
}()
224
264
return xfs .Dir (f , fis ), nil
225
265
}
266
+ /* TODO(xsw):
226
267
if p.cacheFile {
227
268
localFile := filepath.Join(local, name)
228
269
f = &objFile{f, localFile, p.notify}
229
270
}
271
+ */
230
272
return
231
273
}
232
274
233
- func (p * remote ) Init (local string , offline bool ) {
275
+ func (p * remote ) Init (local string , offline bool ) error {
276
+ return nil
234
277
}
235
278
236
279
// -----------------------------------------------------------------------------------------
@@ -239,6 +282,7 @@ type NotifyFile interface {
239
282
NotifyFile (ctx context.Context , name string , fi fs.FileInfo )
240
283
}
241
284
285
+ // NewRemote creates a new remote file system.
242
286
func NewRemote (fsRemote http.FileSystem , notifyOrNil NotifyFile , cacheFile bool ) (ret cached.Remote , err error ) {
243
287
return & remote {fsRemote , notifyOrNil , cacheFile }, nil
244
288
}
0 commit comments