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.
		
		
		
		
		
			
		
			
				
					
					
						
							110 lines
						
					
					
						
							2.8 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							110 lines
						
					
					
						
							2.8 KiB
						
					
					
				| package mount | |
| 
 | |
| import ( | |
| 	"context" | |
| 	"github.com/chrislusf/seaweedfs/weed/filer" | |
| 	"github.com/chrislusf/seaweedfs/weed/glog" | |
| 	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb" | |
| 	"github.com/hanwen/go-fuse/v2/fuse" | |
| 	"syscall" | |
| 	"time" | |
| ) | |
| 
 | |
| /* | |
| What is an inode? | |
| If the file is an hardlinked file: | |
| 	use the hardlink id as inode | |
| Otherwise: | |
| 	use the file path as inode | |
|  | |
| When creating a link: | |
| 	use the original file inode | |
| */ | |
| 
 | |
| /** Create a hard link to a file */ | |
| func (wfs *WFS) Link(cancel <-chan struct{}, in *fuse.LinkIn, name string, out *fuse.EntryOut) (code fuse.Status) { | |
| 
 | |
| 	if wfs.IsOverQuota { | |
| 		return fuse.Status(syscall.ENOSPC) | |
| 	} | |
| 
 | |
| 	if s := checkName(name); s != fuse.OK { | |
| 		return s | |
| 	} | |
| 
 | |
| 	newParentPath, code := wfs.inodeToPath.GetPath(in.NodeId) | |
| 	if code != fuse.OK { | |
| 		return | |
| 	} | |
| 	oldEntryPath, code := wfs.inodeToPath.GetPath(in.Oldnodeid) | |
| 	if code != fuse.OK { | |
| 		return | |
| 	} | |
| 	oldParentPath, _ := oldEntryPath.DirAndName() | |
| 
 | |
| 	oldEntry, status := wfs.maybeLoadEntry(oldEntryPath) | |
| 	if status != fuse.OK { | |
| 		return status | |
| 	} | |
| 
 | |
| 	// update old file to hardlink mode | |
| 	if len(oldEntry.HardLinkId) == 0 { | |
| 		oldEntry.HardLinkId = filer.NewHardLinkId() | |
| 		oldEntry.HardLinkCounter = 1 | |
| 	} | |
| 	oldEntry.HardLinkCounter++ | |
| 	updateOldEntryRequest := &filer_pb.UpdateEntryRequest{ | |
| 		Directory:  oldParentPath, | |
| 		Entry:      oldEntry, | |
| 		Signatures: []int32{wfs.signature}, | |
| 	} | |
| 
 | |
| 	// CreateLink 1.2 : update new file to hardlink mode | |
| 	oldEntry.Attributes.Mtime = time.Now().Unix() | |
| 	request := &filer_pb.CreateEntryRequest{ | |
| 		Directory: string(newParentPath), | |
| 		Entry: &filer_pb.Entry{ | |
| 			Name:            name, | |
| 			IsDirectory:     false, | |
| 			Attributes:      oldEntry.Attributes, | |
| 			Chunks:          oldEntry.Chunks, | |
| 			Extended:        oldEntry.Extended, | |
| 			HardLinkId:      oldEntry.HardLinkId, | |
| 			HardLinkCounter: oldEntry.HardLinkCounter, | |
| 		}, | |
| 		Signatures: []int32{wfs.signature}, | |
| 	} | |
| 
 | |
| 	// apply changes to the filer, and also apply to local metaCache | |
| 	err := wfs.WithFilerClient(false, func(client filer_pb.SeaweedFilerClient) error { | |
| 
 | |
| 		wfs.mapPbIdFromLocalToFiler(request.Entry) | |
| 		defer wfs.mapPbIdFromFilerToLocal(request.Entry) | |
| 
 | |
| 		if err := filer_pb.UpdateEntry(client, updateOldEntryRequest); err != nil { | |
| 			return err | |
| 		} | |
| 		wfs.metaCache.UpdateEntry(context.Background(), filer.FromPbEntry(updateOldEntryRequest.Directory, updateOldEntryRequest.Entry)) | |
| 
 | |
| 		if err := filer_pb.CreateEntry(client, request); err != nil { | |
| 			return err | |
| 		} | |
| 
 | |
| 		wfs.metaCache.InsertEntry(context.Background(), filer.FromPbEntry(request.Directory, request.Entry)) | |
| 
 | |
| 		return nil | |
| 	}) | |
| 
 | |
| 	newEntryPath := newParentPath.Child(name) | |
| 
 | |
| 	if err != nil { | |
| 		glog.V(0).Infof("Link %v -> %s: %v", oldEntryPath, newEntryPath, err) | |
| 		return fuse.EIO | |
| 	} | |
| 
 | |
| 	inode := wfs.inodeToPath.Lookup(newEntryPath, oldEntry.Attributes.Crtime, oldEntry.IsDirectory, true, oldEntry.Attributes.Inode, true) | |
| 
 | |
| 	wfs.outputPbEntry(out, inode, request.Entry) | |
| 
 | |
| 	return fuse.OK | |
| }
 |