func (d *driver) deleteFromDir(diffInfo *pfs.DiffInfo, child *pfs.File, shard uint64) { childPath := child.Path dirPath := path.Dir(childPath) _append, ok := diffInfo.Appends[dirPath] if !ok { _append = newAppend(pfs.FileType_FILE_TYPE_DIR) diffInfo.Appends[dirPath] = _append } if _append.Children == nil { _append.Children = make(map[string]bool) } // Basically, we only set the entry to false if it's not been // set to true. If it's been set to true, that means that there // is a PutFile operation in this commit for this very same file, // so we don't want to remove the file from the directory. if !_append.Children[childPath] { _append.Children[childPath] = false if diffInfo.ParentCommit != nil { _append.LastRef = d.lastRef( client.NewFile(diffInfo.ParentCommit.Repo.Name, diffInfo.ParentCommit.ID, dirPath), shard, ) } } }
func (d *driver) addDirs(diffInfo *pfs.DiffInfo, child *pfs.File, shard uint64) { childPath := child.Path dirPath := path.Dir(childPath) for { _append, ok := diffInfo.Appends[dirPath] if !ok { _append = newAppend(pfs.FileType_FILE_TYPE_DIR) diffInfo.Appends[dirPath] = _append } if _append.Children == nil { _append.Children = make(map[string]bool) } _append.Children[childPath] = true if diffInfo.ParentCommit != nil { _append.LastRef = d.lastRef( client.NewFile(diffInfo.ParentCommit.Repo.Name, diffInfo.ParentCommit.ID, dirPath), shard, ) } if dirPath == "." { break } childPath = dirPath dirPath = path.Dir(childPath) } }
func (d *driver) MakeDirectory(file *pfs.File, shard uint64) (retErr error) { defer func() { if retErr == nil { metrics.AddFiles(1) } }() d.lock.Lock() defer d.lock.Unlock() fileType, err := d.getFileType(file, shard) if err != nil { return err } if fileType == pfs.FileType_FILE_TYPE_REGULAR { return fmt.Errorf("%s already exists and is a file", file.Path) } else if fileType == pfs.FileType_FILE_TYPE_DIR { return nil } canonicalCommit, err := d.canonicalCommit(file.Commit) if err != nil { return err } diffInfo, ok := d.diffs.get(client.NewDiff(canonicalCommit.Repo.Name, canonicalCommit.ID, shard)) if !ok { return pfsserver.NewErrCommitNotFound(canonicalCommit.Repo.Name, canonicalCommit.ID) } if diffInfo.Finished != nil { return fmt.Errorf("commit %s/%s has already been finished", canonicalCommit.Repo.Name, canonicalCommit.ID) } d.addDirs(diffInfo, file, shard) _append, ok := diffInfo.Appends[path.Clean(file.Path)] if !ok { _append = newAppend(pfs.FileType_FILE_TYPE_DIR) } else { _append.FileType = pfs.FileType_FILE_TYPE_DIR } if diffInfo.ParentCommit != nil { _append.LastRef = d.lastRef( client.NewFile( diffInfo.ParentCommit.Repo.Name, diffInfo.ParentCommit.ID, file.Path, ), shard, ) } diffInfo.Appends[path.Clean(file.Path)] = _append // The fact that this is a directory is signified by setting Children // to non-nil _append.Children = make(map[string]bool) return nil }
// If recurse is set to true, and if the file being inspected is a directory, // its children will have the correct sizes. If recurse is false and the file // is a directory, its children will have size of 0. // If unsafe is set to true, you can inspect files in an open commit func (d *driver) inspectFile(file *pfs.File, filterShard *pfs.Shard, shard uint64, from *pfs.Commit, recurse bool, unsafe bool, handle string) (*pfs.FileInfo, []*pfs.BlockRef, error) { fileInfo := &pfs.FileInfo{File: file} var blockRefs []*pfs.BlockRef children := make(map[string]bool) deletedChildren := make(map[string]bool) commit, err := d.canonicalCommit(file.Commit) if err != nil { return nil, nil, err } for commit != nil && (from == nil || commit.ID != from.ID) { diffInfo, ok := d.diffs.get(client.NewDiff(commit.Repo.Name, commit.ID, shard)) if !ok { return nil, nil, pfsserver.NewErrCommitNotFound(commit.Repo.Name, commit.ID) } if !unsafe && diffInfo.Finished == nil { commit = diffInfo.ParentCommit continue } if _append, ok := diffInfo.Appends[path.Clean(file.Path)]; ok { if _append.FileType == pfs.FileType_FILE_TYPE_NONE && !_append.Delete && len(_append.HandleDeletes) == 0 { return nil, nil, fmt.Errorf("the append for %s has file type NONE, this is likely a bug", path.Clean(file.Path)) } if _append.FileType == pfs.FileType_FILE_TYPE_REGULAR { if fileInfo.FileType == pfs.FileType_FILE_TYPE_DIR { return nil, nil, fmt.Errorf("mixed dir and regular file %s/%s/%s, (this is likely a bug)", file.Commit.Repo.Name, file.Commit.ID, file.Path) } if fileInfo.FileType == pfs.FileType_FILE_TYPE_NONE { // the first time we find out it's a regular file we check // the file shard, dirs get returned regardless of sharding, // since they might have children from any shard if !pfsserver.FileInShard(filterShard, file) { return nil, nil, pfsserver.NewErrFileNotFound(file.Path, file.Commit.Repo.Name, file.Commit.ID) } } fileInfo.FileType = pfs.FileType_FILE_TYPE_REGULAR filtered := filterBlockRefs(filterShard, _append.BlockRefs) if handle == "" { for _, handleBlockRefs := range _append.Handles { filtered = append(filtered, filterBlockRefs(filterShard, handleBlockRefs.BlockRef)...) } } else { if handleBlockRefs, ok := _append.Handles[handle]; ok { filtered = append(filtered, filterBlockRefs(filterShard, handleBlockRefs.BlockRef)...) } } blockRefs = append(filtered, blockRefs...) for _, blockRef := range filtered { fileInfo.SizeBytes += (blockRef.Range.Upper - blockRef.Range.Lower) } } else if _append.FileType == pfs.FileType_FILE_TYPE_DIR { if fileInfo.FileType == pfs.FileType_FILE_TYPE_REGULAR { return nil, nil, fmt.Errorf("mixed dir and regular file %s/%s/%s, (this is likely a bug)", file.Commit.Repo.Name, file.Commit.ID, file.Path) } fileInfo.FileType = pfs.FileType_FILE_TYPE_DIR for child, add := range _append.Children { if !add { deletedChildren[child] = true continue } if !children[child] && !deletedChildren[child] { childFile := client.NewFile(commit.Repo.Name, commit.ID, child) if pfsserver.FileInShard(filterShard, childFile) { fileInfo.Children = append( fileInfo.Children, client.NewFile(commit.Repo.Name, commit.ID, child), ) if recurse { childFileInfo, _, err := d.inspectFile(&pfs.File{ Commit: file.Commit, Path: child, }, filterShard, shard, from, recurse, unsafe, handle) if err != nil { return nil, nil, err } fileInfo.SizeBytes += childFileInfo.SizeBytes } } } children[child] = true } } // If Delete is true, then everything before this commit is irrelevant if _append.Delete || (unsafe && handle != "" && _append.HandleDeletes[handle]) { break } if fileInfo.CommitModified == nil { fileInfo.CommitModified = commit fileInfo.Modified = diffInfo.Finished } commit = _append.LastRef continue } commit = diffInfo.ParentCommit } if fileInfo.FileType == pfs.FileType_FILE_TYPE_NONE { return nil, nil, pfsserver.NewErrFileNotFound(file.Path, file.Commit.Repo.Name, file.Commit.ID) } return fileInfo, blockRefs, nil }
func (d *driver) PutFile(file *pfs.File, handle string, delimiter pfs.Delimiter, shard uint64, reader io.Reader) (retErr error) { blockClient, err := d.getBlockClient() if err != nil { return err } _client := client.APIClient{BlockAPIClient: blockClient} blockRefs, err := _client.PutBlock(delimiter, reader) if err != nil { return err } defer func() { if retErr == nil { metrics.AddFiles(1) for _, blockRef := range blockRefs.BlockRef { metrics.AddBytes(int64(blockRef.Range.Upper - blockRef.Range.Lower)) } } }() d.lock.Lock() defer d.lock.Unlock() fileType, err := d.getFileType(file, shard) if err != nil { return err } if fileType == pfs.FileType_FILE_TYPE_DIR { return fmt.Errorf("%s is a directory", file.Path) } canonicalCommit, err := d.canonicalCommit(file.Commit) if err != nil { return err } diffInfo, ok := d.diffs.get(client.NewDiff(canonicalCommit.Repo.Name, canonicalCommit.ID, shard)) if !ok { // This is a weird case since the commit existed above, it means someone // deleted the commit while the above code was running return pfsserver.NewErrCommitNotFound(canonicalCommit.Repo.Name, canonicalCommit.ID) } if diffInfo.Finished != nil { return fmt.Errorf("commit %s/%s has already been finished", canonicalCommit.Repo.Name, canonicalCommit.ID) } d.addDirs(diffInfo, file, shard) _append, ok := diffInfo.Appends[path.Clean(file.Path)] if !ok { _append = newAppend(pfs.FileType_FILE_TYPE_REGULAR) } else { _append.FileType = pfs.FileType_FILE_TYPE_REGULAR } if diffInfo.ParentCommit != nil { _append.LastRef = d.lastRef( client.NewFile(diffInfo.ParentCommit.Repo.Name, diffInfo.ParentCommit.ID, file.Path), shard, ) } diffInfo.Appends[path.Clean(file.Path)] = _append if handle == "" { _append.BlockRefs = append(_append.BlockRefs, blockRefs.BlockRef...) } else { handleBlockRefs, ok := _append.Handles[handle] if !ok { handleBlockRefs = &pfs.BlockRefs{} _append.Handles[handle] = handleBlockRefs } handleBlockRefs.BlockRef = append(handleBlockRefs.BlockRef, blockRefs.BlockRef...) } for _, blockRef := range blockRefs.BlockRef { diffInfo.SizeBytes += blockRef.Range.Upper - blockRef.Range.Lower } return nil }