You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							186 lines
						
					
					
						
							5.3 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							186 lines
						
					
					
						
							5.3 KiB
						
					
					
				
								package mount
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"context"
							 | 
						|
									"fmt"
							 | 
						|
									"syscall"
							 | 
						|
									"time"
							 | 
						|
								
							 | 
						|
									"github.com/hanwen/go-fuse/v2/fuse"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/filer"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/glog"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/pb/filer_pb"
							 | 
						|
									"github.com/seaweedfs/seaweedfs/weed/util"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Flush method
							 | 
						|
								 *
							 | 
						|
								 * This is called on each close() of the opened file.
							 | 
						|
								 *
							 | 
						|
								 * Since file descriptors can be duplicated (dup, dup2, fork), for
							 | 
						|
								 * one open call there may be many flush calls.
							 | 
						|
								 *
							 | 
						|
								 * Filesystems shouldn't assume that flush will always be called
							 | 
						|
								 * after some writes, or that if will be called at all.
							 | 
						|
								 *
							 | 
						|
								 * fi->fh will contain the value set by the open method, or will
							 | 
						|
								 * be undefined if the open method didn't set any value.
							 | 
						|
								 *
							 | 
						|
								 * NOTE: the name of the method is misleading, since (unlike
							 | 
						|
								 * fsync) the filesystem is not forced to flush pending writes.
							 | 
						|
								 * One reason to flush data is if the filesystem wants to return
							 | 
						|
								 * write errors during close.  However, such use is non-portable
							 | 
						|
								 * because POSIX does not require [close] to wait for delayed I/O to
							 | 
						|
								 * complete.
							 | 
						|
								 *
							 | 
						|
								 * If the filesystem supports file locking operations (setlk,
							 | 
						|
								 * getlk) it should remove all locks belonging to 'fi->owner'.
							 | 
						|
								 *
							 | 
						|
								 * If this request is answered with an error code of ENOSYS,
							 | 
						|
								 * this is treated as success and future calls to flush() will
							 | 
						|
								 * succeed automatically without being send to the filesystem
							 | 
						|
								 * process.
							 | 
						|
								 *
							 | 
						|
								 * Valid replies:
							 | 
						|
								 *   fuse_reply_err
							 | 
						|
								 *
							 | 
						|
								 * @param req request handle
							 | 
						|
								 * @param ino the inode number
							 | 
						|
								 * @param fi file information
							 | 
						|
								 *
							 | 
						|
								 * [close]: http://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html
							 | 
						|
								 */
							 | 
						|
								func (wfs *WFS) Flush(cancel <-chan struct{}, in *fuse.FlushIn) fuse.Status {
							 | 
						|
									fh := wfs.GetHandle(FileHandleId(in.Fh))
							 | 
						|
									if fh == nil {
							 | 
						|
										// If handle is not found, it might have been already released
							 | 
						|
										// This is not an error condition for FLUSH
							 | 
						|
										return fuse.OK
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return wfs.doFlush(fh, in.Uid, in.Gid)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								/**
							 | 
						|
								 * Synchronize file contents
							 | 
						|
								 *
							 | 
						|
								 * If the datasync parameter is non-zero, then only the user data
							 | 
						|
								 * should be flushed, not the meta data.
							 | 
						|
								 *
							 | 
						|
								 * If this request is answered with an error code of ENOSYS,
							 | 
						|
								 * this is treated as success and future calls to fsync() will
							 | 
						|
								 * succeed automatically without being send to the filesystem
							 | 
						|
								 * process.
							 | 
						|
								 *
							 | 
						|
								 * Valid replies:
							 | 
						|
								 *   fuse_reply_err
							 | 
						|
								 *
							 | 
						|
								 * @param req request handle
							 | 
						|
								 * @param ino the inode number
							 | 
						|
								 * @param datasync flag indicating if only data should be flushed
							 | 
						|
								 * @param fi file information
							 | 
						|
								 */
							 | 
						|
								func (wfs *WFS) Fsync(cancel <-chan struct{}, in *fuse.FsyncIn) (code fuse.Status) {
							 | 
						|
								
							 | 
						|
									fh := wfs.GetHandle(FileHandleId(in.Fh))
							 | 
						|
									if fh == nil {
							 | 
						|
										return fuse.ENOENT
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return wfs.doFlush(fh, in.Uid, in.Gid)
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (wfs *WFS) doFlush(fh *FileHandle, uid, gid uint32) fuse.Status {
							 | 
						|
								
							 | 
						|
									// flush works at fh level
							 | 
						|
									fileFullPath := fh.FullPath()
							 | 
						|
									dir, name := fileFullPath.DirAndName()
							 | 
						|
									// send the data to the OS
							 | 
						|
									glog.V(4).Infof("doFlush %s fh %d", fileFullPath, fh.fh)
							 | 
						|
								
							 | 
						|
									if !wfs.IsOverQuota {
							 | 
						|
										if err := fh.dirtyPages.FlushData(); err != nil {
							 | 
						|
											glog.Errorf("%v doFlush: %v", fileFullPath, err)
							 | 
						|
											return fuse.EIO
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if !fh.dirtyMetadata {
							 | 
						|
										return fuse.OK
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if wfs.IsOverQuota {
							 | 
						|
										return fuse.Status(syscall.ENOSPC)
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									fhActiveLock := fh.wfs.fhLockTable.AcquireLock("doFlush", fh.fh, util.ExclusiveLock)
							 | 
						|
									defer fh.wfs.fhLockTable.ReleaseLock(fh.fh, fhActiveLock)
							 | 
						|
								
							 | 
						|
									err := wfs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error {
							 | 
						|
								
							 | 
						|
										entry := fh.GetEntry()
							 | 
						|
										entry.Name = name // this flush may be just after a rename operation
							 | 
						|
								
							 | 
						|
										if entry.Attributes != nil {
							 | 
						|
											entry.Attributes.Mime = fh.contentType
							 | 
						|
											if entry.Attributes.Uid == 0 {
							 | 
						|
												entry.Attributes.Uid = uid
							 | 
						|
											}
							 | 
						|
											if entry.Attributes.Gid == 0 {
							 | 
						|
												entry.Attributes.Gid = gid
							 | 
						|
											}
							 | 
						|
											entry.Attributes.Mtime = time.Now().Unix()
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										request := &filer_pb.CreateEntryRequest{
							 | 
						|
											Directory:                string(dir),
							 | 
						|
											Entry:                    entry.GetEntry(),
							 | 
						|
											Signatures:               []int32{wfs.signature},
							 | 
						|
											SkipCheckParentDirectory: true,
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										glog.V(4).Infof("%s set chunks: %v", fileFullPath, len(entry.GetChunks()))
							 | 
						|
										//for i, chunk := range entry.GetChunks() {
							 | 
						|
										//	glog.V(4).Infof("%s chunks %d: %v [%d,%d)", fileFullPath, i, chunk.GetFileIdString(), chunk.Offset, chunk.Offset+int64(chunk.Size))
							 | 
						|
										//}
							 | 
						|
								
							 | 
						|
										manifestChunks, nonManifestChunks := filer.SeparateManifestChunks(entry.GetChunks())
							 | 
						|
								
							 | 
						|
										chunks, _ := filer.CompactFileChunks(context.Background(), wfs.LookupFn(), nonManifestChunks)
							 | 
						|
										chunks, manifestErr := filer.MaybeManifestize(wfs.saveDataAsChunk(fileFullPath), chunks)
							 | 
						|
										if manifestErr != nil {
							 | 
						|
											// not good, but should be ok
							 | 
						|
											glog.V(0).Infof("MaybeManifestize: %v", manifestErr)
							 | 
						|
										}
							 | 
						|
										entry.Chunks = append(chunks, manifestChunks...)
							 | 
						|
								
							 | 
						|
										wfs.mapPbIdFromLocalToFiler(request.Entry)
							 | 
						|
										defer wfs.mapPbIdFromFilerToLocal(request.Entry)
							 | 
						|
								
							 | 
						|
										if err := filer_pb.CreateEntry(context.Background(), client, request); err != nil {
							 | 
						|
											glog.Errorf("fh flush create %s: %v", fileFullPath, err)
							 | 
						|
											return fmt.Errorf("fh flush create %s: %v", fileFullPath, err)
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										wfs.metaCache.InsertEntry(context.Background(), filer.FromPbEntry(request.Directory, request.Entry))
							 | 
						|
								
							 | 
						|
										return nil
							 | 
						|
									})
							 | 
						|
								
							 | 
						|
									if err == nil {
							 | 
						|
										fh.dirtyMetadata = false
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if err != nil {
							 | 
						|
										glog.Errorf("%v fh %d flush: %v", fileFullPath, fh.fh, err)
							 | 
						|
										return fuse.EIO
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if IsDebugFileReadWrite {
							 | 
						|
										fh.mirrorFile.Sync()
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return fuse.OK
							 | 
						|
								}
							 |