@@ -47,6 +47,8 @@ func (file *FileINode) AbsolutePath() string {
47
47
48
48
// Responds to the FUSE file attribute request
49
49
func (file * FileINode ) Attr (ctx context.Context , a * fuse.Attr ) error {
50
+ file .fileMutex .Lock ()
51
+ defer file .fileMutex .Unlock ()
50
52
if file .FileSystem .Clock .Now ().After (file .Attrs .Expires ) {
51
53
err := file .Parent .LookupAttrs (file .Attrs .Name , & file .Attrs )
52
54
if err != nil {
@@ -100,6 +102,8 @@ func (file *FileINode) RemoveHandle(handle *FileHandle) {
100
102
// Responds to the FUSE Fsync request
101
103
func (file * FileINode ) Fsync (ctx context.Context , req * fuse.FsyncRequest ) error {
102
104
loginfo (fmt .Sprintf ("Dispatching fsync request to all open handles: %d" , len (file .activeHandles )), Fields {Operation : Fsync })
105
+ file .fileMutex .Lock ()
106
+ defer file .fileMutex .Unlock ()
103
107
var retErr error
104
108
for _ , handle := range file .activeHandles {
105
109
err := handle .Fsync (ctx , req )
@@ -123,7 +127,7 @@ func (file *FileINode) Setattr(ctx context.Context, req *fuse.SetattrRequest, re
123
127
if req .Valid .Size () {
124
128
var retErr error
125
129
for _ , handle := range file .activeHandles {
126
- if handle .isWriteable () { // to only write enabled handles
130
+ if handle .dataChanged () { // to only write enabled handles
127
131
err := handle .Truncate (int64 (req .Size ))
128
132
if err != nil {
129
133
retErr = err
@@ -188,9 +192,9 @@ func (file *FileINode) countActiveHandles() int {
188
192
return len (file .activeHandles )
189
193
}
190
194
191
- func (file * FileINode ) createStagingFile (operation string , existsInDFS bool ) error {
195
+ func (file * FileINode ) createStagingFile (operation string , existsInDFS bool ) ( * os. File , error ) {
192
196
if file .handle != nil {
193
- return nil // there is already an active handle.
197
+ return nil , nil // there is already an active handle.
194
198
}
195
199
196
200
//create staging file
@@ -200,7 +204,7 @@ func (file *FileINode) createStagingFile(operation string, existsInDFS bool) err
200
204
w , err := hdfsAccessor .CreateFile (absPath , file .Attrs .Mode , false )
201
205
if err != nil {
202
206
logerror ("Failed to create file in DFS" , file .logInfo (Fields {Operation : operation , Error : err }))
203
- return err
207
+ return nil , err
204
208
}
205
209
loginfo ("Created an empty file in DFS" , file .logInfo (Fields {Operation : operation }))
206
210
w .Close ()
@@ -209,25 +213,24 @@ func (file *FileINode) createStagingFile(operation string, existsInDFS bool) err
209
213
_ , err := hdfsAccessor .Stat (absPath )
210
214
if err != nil {
211
215
logerror ("Failed to stat file in DFS" , file .logInfo (Fields {Operation : operation , Error : err }))
212
- return syscall .ENOENT
216
+ return nil , syscall .ENOENT
213
217
}
214
218
}
215
219
216
220
stagingFile , err := ioutil .TempFile (stagingDir , "stage" )
217
221
if err != nil {
218
222
logerror ("Failed to create staging file" , file .logInfo (Fields {Operation : operation , Error : err }))
219
- return err
223
+ return nil , err
220
224
}
221
225
os .Remove (stagingFile .Name ())
222
226
loginfo ("Created staging file" , file .logInfo (Fields {Operation : operation , TmpFile : stagingFile .Name ()}))
223
227
224
228
if existsInDFS {
225
229
if err := file .downloadToStaging (stagingFile , operation ); err != nil {
226
- return err
230
+ return nil , err
227
231
}
228
232
}
229
- file .handle = & LocalFileProxy {localFile : stagingFile , file : file }
230
- return nil
233
+ return stagingFile , nil
231
234
}
232
235
233
236
func (file * FileINode ) downloadToStaging (stagingFile * os.File , operation string ) error {
@@ -256,22 +259,77 @@ func (file *FileINode) NewFileHandle(existsInDFS bool, flags fuse.OpenFlags) (*F
256
259
file .lockFileHandle ()
257
260
defer file .unLockFileHandle ()
258
261
262
+ fh := & FileHandle {File : file , fileFlags : flags , fhID : int64 (rand .Uint64 ())}
259
263
operation := Create
260
264
if existsInDFS {
261
265
operation = Open
262
266
}
263
267
264
- fh := & FileHandle {File : file , fileFlags : flags , fhID : int64 (rand .Uint64 ())}
265
- if err := file .checkDiskSpace (); err != nil {
266
- return nil , err
268
+ if operation == Create {
269
+ // there must be no existing file handles for create operation
270
+ if file .handle != nil {
271
+ logpanic ("Unexpected file state during creation" , file .logInfo (Fields {Flags : flags }))
272
+ }
273
+ if err := file .checkDiskSpace (); err != nil {
274
+ return nil , err
275
+ }
276
+ stagingFile , err := file .createStagingFile (operation , existsInDFS )
277
+ if err != nil {
278
+ return nil , err
279
+ }
280
+ fh .File .handle = & LocalRWFileProxy {localFile : stagingFile , file : file }
281
+ loginfo ("Opened file, RW handle" , fh .logInfo (Fields {Operation : operation , Flags : fh .fileFlags }))
282
+ } else {
283
+ if file .handle != nil {
284
+ fh .File .handle = file .handle
285
+ loginfo ("Opened file, Returning existing handle" , fh .logInfo (Fields {Operation : operation , Flags : fh .fileFlags }))
286
+ } else {
287
+ // we alway open the file in RO mode. when the client writes to the file
288
+ // then we upgrade the handle. However, if the file is already opened in
289
+ // in RW state then we use the existing RW handle
290
+ // if file.handle
291
+ reader , _ := file .FileSystem .HdfsAccessor .OpenRead (file .AbsolutePath ())
292
+ fh .File .handle = & RemoteROFileProxy {hdfsReader : reader , file : file }
293
+ loginfo ("Opened file, RO handle" , fh .logInfo (Fields {Operation : operation , Flags : fh .fileFlags }))
294
+ }
267
295
}
296
+ return fh , nil
297
+ }
268
298
269
- if err := file .createStagingFile (operation , existsInDFS ); err != nil {
270
- return nil , err
299
+ // changes RO file handle to RW
300
+ func (file * FileINode ) upgradeHandleForWriting () error {
301
+ file .lockFileHandle ()
302
+ defer file .unLockFileHandle ()
303
+
304
+ var upgrade = false
305
+ if _ , ok := file .handle .(* LocalRWFileProxy ); ok {
306
+ upgrade = false
307
+ } else if _ , ok := file .handle .(* RemoteROFileProxy ); ok {
308
+ upgrade = true
309
+ } else {
310
+ logpanic ("Unrecognized remote file proxy" , nil )
271
311
}
272
312
273
- loginfo ("Opened file" , fh .logInfo (Fields {Operation : operation , Flags : fh .fileFlags }))
274
- return fh , nil
313
+ if ! upgrade {
314
+ return nil
315
+ } else {
316
+ remoteROFileProxy , _ := file .handle .(* RemoteROFileProxy )
317
+ remoteROFileProxy .hdfsReader .Close () // close this read only handle
318
+ file .handle = nil
319
+
320
+ if err := file .checkDiskSpace (); err != nil {
321
+ return err
322
+ }
323
+
324
+ stagingFile , err := file .createStagingFile ("Open" , true )
325
+ if err != nil {
326
+ return err
327
+ }
328
+
329
+ file .handle = & LocalRWFileProxy {localFile : stagingFile , file : file }
330
+ loginfo ("Open handle upgrade to support RW " , file .logInfo (Fields {Operation : "Open" }))
331
+ return nil
332
+ }
275
333
}
276
334
277
335
func (file * FileINode ) checkDiskSpace () error {
0 commit comments