github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/kbfs/data/path.go (about)

     1  // Copyright 2016 Keybase Inc. All rights reserved.
     2  // Use of this source code is governed by a BSD
     3  // license that can be found in the LICENSE file.
     4  
     5  package data
     6  
     7  import (
     8  	"fmt"
     9  	"strings"
    10  
    11  	"github.com/keybase/client/go/kbfs/tlf"
    12  	"github.com/keybase/client/go/kbfs/tlfhandle"
    13  )
    14  
    15  // Path represents the full KBFS path to a particular location, so
    16  // that a flush can traverse backwards and fix up ids along the way.
    17  type Path struct {
    18  	FolderBranch
    19  	Path            []PathNode
    20  	ChildObfuscator Obfuscator
    21  }
    22  
    23  // IsValid returns true if the path has at least one node (for the
    24  // root).
    25  func (p Path) IsValid() bool {
    26  	if p.Tlf == tlf.NullID {
    27  		return false
    28  	}
    29  
    30  	if len(p.Path) < 1 {
    31  		return false
    32  	}
    33  
    34  	for _, n := range p.Path {
    35  		if !n.IsValid() {
    36  			return false
    37  		}
    38  	}
    39  
    40  	return true
    41  }
    42  
    43  // IsValidForNotification returns true if the path has at least one
    44  // node (for the root), and the first element of the path is non-empty
    45  // and does not start with "<", which indicates an unnotifiable path.
    46  func (p Path) IsValidForNotification() bool {
    47  	if !p.IsValid() {
    48  		return false
    49  	}
    50  
    51  	if p.Tlf == (tlf.NullID) {
    52  		return false
    53  	}
    54  
    55  	namePlain := p.Path[0].Name.Plaintext()
    56  	return len(namePlain) > 0 && !strings.HasPrefix(namePlain, "<")
    57  }
    58  
    59  // HasValidParent returns true if this path is valid and
    60  // `ParentPath()` is a valid path.
    61  func (p Path) HasValidParent() bool {
    62  	return len(p.Path) >= 2 && p.ParentPath().IsValid()
    63  }
    64  
    65  // TailName returns the name of the final node in the Path. Must be
    66  // called with a valid path.
    67  func (p Path) TailName() PathPartString {
    68  	return p.Path[len(p.Path)-1].Name
    69  }
    70  
    71  // TailPointer returns the BlockPointer of the final node in the Path.
    72  // Must be called with a valid path.
    73  func (p Path) TailPointer() BlockPointer {
    74  	return p.Path[len(p.Path)-1].BlockPointer
    75  }
    76  
    77  // TailRef returns the BlockRef of the final node in the Path.  Must
    78  // be called with a valid path.
    79  func (p Path) TailRef() BlockRef {
    80  	return p.Path[len(p.Path)-1].Ref()
    81  }
    82  
    83  // DebugString returns a string representation of the path with all
    84  // branch and pointer information.
    85  func (p Path) DebugString() string {
    86  	debugNames := make([]string, 0, len(p.Path))
    87  	for _, node := range p.Path {
    88  		debugNames = append(debugNames, node.DebugString())
    89  	}
    90  	return fmt.Sprintf("%s:%s", p.FolderBranch, strings.Join(debugNames, "/"))
    91  }
    92  
    93  // String implements the fmt.Stringer interface for Path.
    94  func (p Path) String() string {
    95  	names := make([]string, 0, len(p.Path))
    96  	for _, node := range p.Path {
    97  		names = append(names, node.Name.String())
    98  	}
    99  	return strings.Join(names, "/")
   100  }
   101  
   102  // Plaintext returns an unobfuscated string for this path.
   103  func (p Path) Plaintext() string {
   104  	names := make([]string, 0, len(p.Path))
   105  	for _, node := range p.Path {
   106  		names = append(names, node.Name.Plaintext())
   107  	}
   108  	return strings.Join(names, "/")
   109  }
   110  
   111  // PlaintextSansTlf returns an unobfuscated string for this path, rooted at the
   112  // TLF.
   113  //
   114  // Examples: /keybase/private/alice -> "/", true
   115  //
   116  //	/keybase/private/alice/folder -> "/folder", true
   117  //	/keybase/private -> "", false
   118  func (p Path) PlaintextSansTlf() (string, bool) {
   119  	if len(p.Path) == 0 {
   120  		return "", false
   121  	}
   122  	names := make([]string, 0, len(p.Path)-1)
   123  	for _, node := range p.Path[1:] {
   124  		names = append(names, node.Name.Plaintext())
   125  	}
   126  	return "/" + strings.Join(names, "/"), true
   127  }
   128  
   129  // CanonicalPathString returns an obfuscated canonical
   130  // representation of the full path, always prefaced by /keybase. This
   131  // may require conversion to a platform specific path, for example, by
   132  // replacing /keybase with the appropriate drive letter on Windows. It
   133  // also, might need conversion if on a different run mode, for
   134  // example, /keybase.staging on Unix type platforms.
   135  func (p Path) CanonicalPathString() string {
   136  	return tlfhandle.BuildCanonicalPathForTlf(p.Tlf, p.String())
   137  }
   138  
   139  // CanonicalPathPlaintext returns an un-obfuscated canonical
   140  // representation of the full path, always prefaced by /keybase. This
   141  // may require conversion to a platform specific path, for example, by
   142  // replacing /keybase with the appropriate drive letter on Windows. It
   143  // also, might need conversion if on a different run mode, for
   144  // example, /keybase.staging on Unix type platforms.
   145  func (p Path) CanonicalPathPlaintext() string {
   146  	return tlfhandle.BuildCanonicalPathForTlf(p.Tlf, p.Plaintext())
   147  }
   148  
   149  // ParentPath returns a new Path representing the parent subdirectory
   150  // of this Path. Must be called with a valid path. Should not be
   151  // called with a path of only a single node, as that would produce an
   152  // invalid path.
   153  func (p Path) ParentPath() *Path {
   154  	return &Path{
   155  		FolderBranch:    p.FolderBranch,
   156  		Path:            p.Path[:len(p.Path)-1],
   157  		ChildObfuscator: p.Path[len(p.Path)-1].Name.Obfuscator(),
   158  	}
   159  }
   160  
   161  // ChildPath returns a new Path with the addition of a new entry
   162  // with the given name and BlockPointer.
   163  func (p Path) ChildPath(
   164  	name PathPartString, ptr BlockPointer, childObfuscator Obfuscator) Path {
   165  	child := Path{
   166  		FolderBranch:    p.FolderBranch,
   167  		Path:            make([]PathNode, len(p.Path), len(p.Path)+1),
   168  		ChildObfuscator: childObfuscator,
   169  	}
   170  	copy(child.Path, p.Path)
   171  	child.Path = append(child.Path, PathNode{Name: name, BlockPointer: ptr})
   172  	return child
   173  }
   174  
   175  // ChildPathNoPtr returns a new Path with the addition of a new entry
   176  // with the given name.  That final PathNode will have no BlockPointer.
   177  func (p Path) ChildPathNoPtr(
   178  	name PathPartString, childObfuscator Obfuscator) Path {
   179  	return p.ChildPath(name, BlockPointer{}, childObfuscator)
   180  }
   181  
   182  // Obfuscator returns the obfuscator of the tail node in this path.
   183  func (p Path) Obfuscator() Obfuscator {
   184  	return p.ChildObfuscator
   185  }
   186  
   187  // PathNode is a single node along an KBFS path, pointing to the top
   188  // block for that node of the path.
   189  type PathNode struct {
   190  	BlockPointer
   191  	Name PathPartString
   192  }
   193  
   194  // IsValid returns true if this node contains a valid block pointer.
   195  func (n PathNode) IsValid() bool {
   196  	return n.BlockPointer.IsValid()
   197  }
   198  
   199  // DebugString returns a string representation of the node with all
   200  // pointer information.
   201  func (n PathNode) DebugString() string {
   202  	return fmt.Sprintf("%s(ptr=%s)", n.Name, n.BlockPointer)
   203  }