func (sub *Sub) compareEntries(request *subproto.UpdateRequest, subEntry, requiredEntry *filesystem.DirectoryEntry, myPathName string) { subInode := subEntry.Inode() requiredInode := requiredEntry.Inode() sameType, sameMetadata, sameData := filesystem.CompareInodes( subInode, requiredInode, nil) if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok { if sameMetadata { return } if sameType { makeDirectory(request, requiredInode, myPathName, false) } else { makeDirectory(request, requiredInode, myPathName, true) } return } if sameType && sameData && sameMetadata { sub.relink(request, subEntry, requiredEntry, myPathName) return } if sameType && sameData { sub.updateMetadata(request, requiredEntry, myPathName) sub.relink(request, subEntry, requiredEntry, myPathName) return } sub.addInode(request, requiredEntry, myPathName) }
func addDirectory(dirent, oldDirent *filesystem.DirectoryEntry, fileSystem, oldFS *FileSystem, directoryPathName string, stat *syscall.Stat_t) error { myPathName := path.Join(directoryPathName, dirent.Name) if stat.Ino == fileSystem.inodeNumber { return errors.New("Recursive directory: " + myPathName) } if _, ok := fileSystem.InodeTable[stat.Ino]; ok { return errors.New("Hardlinked directory: " + myPathName) } inode := new(filesystem.DirectoryInode) dirent.SetInode(inode) fileSystem.InodeTable[stat.Ino] = inode inode.Mode = filesystem.FileMode(stat.Mode) inode.Uid = stat.Uid inode.Gid = stat.Gid var oldInode *filesystem.DirectoryInode if oldDirent != nil { if oi, ok := oldDirent.Inode().(*filesystem.DirectoryInode); ok { oldInode = oi } } err, copied := scanDirectory(inode, oldInode, fileSystem, oldFS, myPathName) if err != nil { return err } if copied && filesystem.CompareDirectoriesMetadata(inode, oldInode, nil) { dirent.SetInode(oldInode) fileSystem.InodeTable[stat.Ino] = oldInode } fileSystem.DirectoryCount++ return nil }
func (sub *Sub) addEntry(request *subproto.UpdateRequest, requiredEntry *filesystem.DirectoryEntry, myPathName string) { requiredInode := requiredEntry.Inode() if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok { makeDirectory(request, requiredInode, myPathName, true) } else { sub.addInode(request, requiredEntry, myPathName) } }
func (sub *Sub) updateMetadata(request *subproto.UpdateRequest, requiredEntry *filesystem.DirectoryEntry, myPathName string) { if sub.inodesChanged[requiredEntry.InodeNumber] { return } var inode subproto.Inode inode.Name = myPathName inode.GenericInode = requiredEntry.Inode() request.InodesToChange = append(request.InodesToChange, inode) sub.inodesChanged[requiredEntry.InodeNumber] = true }
func setComputedFileMtime(requiredInode *filesystem.RegularInode, subEntry *filesystem.DirectoryEntry) { if requiredInode.MtimeSeconds >= 0 { return } if subEntry != nil { subInode := subEntry.Inode() if subInode, ok := subInode.(*filesystem.RegularInode); ok { if requiredInode.Hash == subInode.Hash { requiredInode.MtimeNanoSeconds = subInode.MtimeNanoSeconds requiredInode.MtimeSeconds = subInode.MtimeSeconds return } } } requiredInode.MtimeSeconds = time.Now().Unix() }
func compareDirectories(request *subproto.UpdateRequest, state *state, subDirectory, requiredDirectory *filesystem.DirectoryInode, myPathName string, filter *filter.Filter) { // First look for entries that should be deleted. if subDirectory != nil { for name := range subDirectory.EntriesByName { pathname := path.Join(myPathName, name) if filter.Match(pathname) { continue } if _, ok := requiredDirectory.EntriesByName[name]; !ok { request.PathsToDelete = append(request.PathsToDelete, pathname) } } } for name, requiredEntry := range requiredDirectory.EntriesByName { pathname := path.Join(myPathName, name) if filter.Match(pathname) { continue } var subEntry *filesystem.DirectoryEntry if subDirectory != nil { if se, ok := subDirectory.EntriesByName[name]; ok { subEntry = se } } if subEntry == nil { addEntry(request, state, requiredEntry, pathname) } else { compareEntries(request, state, subEntry, requiredEntry, pathname, filter) } // If a directory: descend (possibly with the directory for the sub). requiredInode := requiredEntry.Inode() if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok { var subInode *filesystem.DirectoryInode if subEntry != nil { if si, ok := subEntry.Inode().(*filesystem.DirectoryInode); ok { subInode = si } } compareDirectories(request, state, subInode, requiredInode, pathname, filter) } } }
func (sub *Sub) addInode(request *subproto.UpdateRequest, requiredEntry *filesystem.DirectoryEntry, myPathName string) { requiredInode := requiredEntry.Inode() if name, ok := sub.inodesCreated[requiredEntry.InodeNumber]; ok { makeHardlink(request, myPathName, name) return } // Try to find a sibling inode. names := sub.requiredFS.InodeToFilenamesTable()[requiredEntry.InodeNumber] subFS := sub.FileSystem if len(names) > 1 { var sameDataInode filesystem.GenericInode var sameDataName string for _, name := range names { if inum, found := subFS.FilenameToInodeTable()[name]; found { subInode := sub.FileSystem.InodeTable[inum] _, sameMetadata, sameData := filesystem.CompareInodes( subInode, requiredInode, nil) if sameMetadata && sameData { makeHardlink(request, myPathName, name) return } if sameData { sameDataInode = subInode sameDataName = name } } } if sameDataInode != nil { sub.updateMetadata(request, requiredEntry, sameDataName) makeHardlink(request, myPathName, sameDataName) return } } if inode, ok := requiredEntry.Inode().(*filesystem.RegularInode); ok { if inode.Size > 0 { if _, ok := sub.subObjectCacheUsage[inode.Hash]; ok { sub.subObjectCacheUsage[inode.Hash]++ } else { // Not in object cache: grab it from file-system. request.FilesToCopyToCache = append( request.FilesToCopyToCache, sub.getFileToCopy(myPathName, inode.Hash)) sub.subObjectCacheUsage[inode.Hash] = 1 } } } var inode subproto.Inode inode.Name = myPathName inode.GenericInode = requiredEntry.Inode() request.InodesToMake = append(request.InodesToMake, inode) sub.inodesCreated[requiredEntry.InodeNumber] = myPathName }
// Returns true if there is a failure due to missing computed files. func (sub *Sub) compareDirectories(request *subproto.UpdateRequest, subDirectory, requiredDirectory *filesystem.DirectoryInode, myPathName string, deleteMissingComputedFiles bool, logger *log.Logger) bool { // First look for entries that should be deleted. if sub.filter != nil && subDirectory != nil { for name := range subDirectory.EntriesByName { pathname := path.Join(myPathName, name) if sub.filter.Match(pathname) { continue } if _, ok := requiredDirectory.EntriesByName[name]; !ok { request.PathsToDelete = append(request.PathsToDelete, pathname) } } } for name, requiredEntry := range requiredDirectory.EntriesByName { pathname := path.Join(myPathName, name) if sub.filter != nil && sub.filter.Match(pathname) { continue } var subEntry *filesystem.DirectoryEntry if subDirectory != nil { if se, ok := subDirectory.EntriesByName[name]; ok { subEntry = se } } requiredInode := requiredEntry.Inode() if _, ok := requiredInode.(*filesystem.ComputedRegularInode); ok { // Replace with computed file. inode, ok := sub.ComputedInodes[pathname] if !ok { if deleteMissingComputedFiles { if subEntry != nil { request.PathsToDelete = append(request.PathsToDelete, pathname) } continue } logger.Printf( "compareDirectories(%s): missing computed file: %s\n", sub, pathname) return true } setComputedFileMtime(inode, subEntry) newEntry := new(filesystem.DirectoryEntry) newEntry.Name = name newEntry.InodeNumber = requiredEntry.InodeNumber newEntry.SetInode(inode) requiredEntry = newEntry } if subEntry == nil { sub.addEntry(request, requiredEntry, pathname) } else { sub.compareEntries(request, subEntry, requiredEntry, pathname) } // If a directory: descend (possibly with the directory for the sub). if requiredInode, ok := requiredInode.(*filesystem.DirectoryInode); ok { var subInode *filesystem.DirectoryInode if subEntry != nil { if si, ok := subEntry.Inode().(*filesystem.DirectoryInode); ok { subInode = si } } sub.compareDirectories(request, subInode, requiredInode, pathname, deleteMissingComputedFiles, logger) } } return false }
func addInode(request *subproto.UpdateRequest, state *state, requiredEntry *filesystem.DirectoryEntry, myPathName string) { requiredInode := requiredEntry.Inode() if name, ok := state.inodesCreated[requiredEntry.InodeNumber]; ok { makeHardlink(request, myPathName, name) return } // Try to find a sibling inode. names := state.requiredFS.InodeToFilenamesTable[requiredEntry.InodeNumber] if len(names) > 1 { var sameDataInode filesystem.GenericInode var sameDataName string for _, name := range names { if inum, found := state.getSubInodeFromFilename(name); found { subInode := state.subFS.InodeTable[inum] _, sameMetadata, sameData := filesystem.CompareInodes( subInode, requiredInode, nil) if sameMetadata && sameData { makeHardlink(request, myPathName, name) return } if sameData { sameDataInode = subInode sameDataName = name } } } if sameDataInode != nil { updateMetadata(request, state, requiredEntry, sameDataName) makeHardlink(request, myPathName, sameDataName) return } } if inode, ok := requiredEntry.Inode().(*filesystem.RegularInode); ok { if inode.Size > 0 { if _, ok := state.subObjectCacheUsage[inode.Hash]; ok { state.subObjectCacheUsage[inode.Hash]++ } else { // Not in object cache: grab it from file-system. if state.subFS.HashToInodesTable == nil { state.subFS.BuildHashToInodesTable() } if ilist, ok := state.subFS.HashToInodesTable[inode.Hash]; ok { var fileToCopy subproto.FileToCopyToCache fileToCopy.Name = state.subFS.InodeToFilenamesTable[ilist[0]][0] fileToCopy.Hash = inode.Hash request.FilesToCopyToCache = append( request.FilesToCopyToCache, fileToCopy) state.subObjectCacheUsage[inode.Hash] = 1 } else { panic("No object in cache for: " + myPathName) } } } } var inode subproto.Inode inode.Name = myPathName inode.GenericInode = requiredEntry.Inode() request.InodesToMake = append(request.InodesToMake, inode) state.inodesCreated[requiredEntry.InodeNumber] = myPathName }