github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfstool/ls.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 main 6 7 import ( 8 "flag" 9 "fmt" 10 "time" 11 12 "github.com/keybase/client/go/kbfs/data" 13 "github.com/keybase/client/go/kbfs/fsrpc" 14 "github.com/keybase/client/go/kbfs/libkbfs" 15 "golang.org/x/net/context" 16 ) 17 18 func printHeader(p fsrpc.Path) { 19 fmt.Printf("%s:\n", p) 20 } 21 22 func computeModeStr(entryType data.EntryType) string { 23 var typeStr string 24 switch entryType { 25 case data.File: 26 typeStr = "-" 27 case data.Exec: 28 typeStr = "-" 29 case data.Dir: 30 typeStr = "d" 31 case data.Sym: 32 typeStr = "l" 33 default: 34 typeStr = "?" 35 } 36 37 // TODO: Figure out whether the current user is just a reader, 38 // and omit w below if so. 39 var modeStr string 40 switch entryType { 41 case data.File: 42 modeStr = "rw-" 43 case data.Exec: 44 modeStr = "rwx" 45 case data.Dir: 46 modeStr = "rwx" 47 case data.Sym: 48 modeStr = "rwx" 49 default: 50 modeStr = "rw-" 51 } 52 53 // TODO: Figure out whether this is a public directory. 54 return fmt.Sprintf("%s%s%s%s", typeStr, modeStr, modeStr, "---") 55 } 56 57 func printEntry(ctx context.Context, config libkbfs.Config, dir fsrpc.Path, name string, entryType data.EntryType, longFormat, useSigil bool) { 58 var sigil string 59 if useSigil { 60 switch entryType { 61 case data.File: 62 case data.Exec: 63 sigil = "*" 64 case data.Dir: 65 sigil = "/" 66 case data.Sym: 67 sigil = "@" 68 default: 69 sigil = "?" 70 } 71 } 72 if longFormat { 73 p, err := dir.Join(name) 74 if err != nil { 75 printError("ls", err) 76 } 77 _, de, err := p.GetNode(ctx, config) 78 if err != nil { 79 printError("ls", err) 80 } 81 82 modeStr := computeModeStr(entryType) 83 mtimeStr := time.Unix(0, de.Mtime).Format("Jan 02 15:04") 84 var symPathStr string 85 if entryType == data.Sym { 86 symPathStr = fmt.Sprintf(" -> %s", de.SymPath) 87 } 88 fmt.Printf("%s\t%d\t%s\t%s%s%s\n", modeStr, de.Size, mtimeStr, name, sigil, symPathStr) 89 } else { 90 fmt.Printf("%s%s\n", name, sigil) 91 } 92 } 93 94 func lsHelper(ctx context.Context, config libkbfs.Config, p fsrpc.Path, hasMultiple bool, handleEntry func(string, data.EntryType)) error { 95 kbfsOps := config.KBFSOps() 96 97 switch p.PathType { 98 case fsrpc.RootPathType: 99 if hasMultiple { 100 printHeader(p) 101 } 102 handleEntry(topName, data.Dir) 103 return nil 104 105 case fsrpc.KeybasePathType: 106 if hasMultiple { 107 printHeader(p) 108 } 109 handleEntry(publicName, data.Dir) 110 handleEntry(privateName, data.Dir) 111 return nil 112 113 case fsrpc.KeybaseChildPathType: 114 favs, err := kbfsOps.GetFavorites(ctx) 115 if err != nil { 116 return err 117 } 118 119 if hasMultiple { 120 printHeader(p) 121 } 122 for _, fav := range favs { 123 if p.TLFType == fav.Type { 124 handleEntry(fav.Name, data.Dir) 125 } 126 } 127 return nil 128 129 case fsrpc.TLFPathType: 130 n, de, err := p.GetNode(ctx, config) 131 if err != nil { 132 return err 133 } 134 135 if de.Type == data.Dir { 136 // GetDirChildren doesn't verify the dir-ness 137 // of the node correctly (since it ends up 138 // creating a new DirBlock if the node isn't 139 // in the cache already). 140 // 141 // TODO: Fix the above. 142 children, err := kbfsOps.GetDirChildren(ctx, n) 143 if err != nil { 144 return err 145 } 146 147 if hasMultiple { 148 printHeader(p) 149 } 150 for name, entryInfo := range children { 151 handleEntry(name.Plaintext(), entryInfo.Type) 152 } 153 } else { 154 _, name, err := p.DirAndBasename() 155 if err != nil { 156 return err 157 } 158 handleEntry(name, de.Type) 159 } 160 return nil 161 162 default: 163 break 164 } 165 166 return fmt.Errorf("invalid KBFS path %s", p) 167 } 168 169 func lsOne(ctx context.Context, config libkbfs.Config, p fsrpc.Path, longFormat, useSigil, recursive, hasMultiple bool, errorFn func(error)) { 170 var children []string 171 handleEntry := func(name string, entryType data.EntryType) { 172 if recursive && entryType == data.Dir { 173 children = append(children, name) 174 } 175 printEntry(ctx, config, p, name, entryType, longFormat, useSigil) 176 } 177 err := lsHelper(ctx, config, p, hasMultiple || recursive, handleEntry) 178 if err != nil { 179 errorFn(err) 180 // Fall-through. 181 } 182 183 if recursive { 184 for _, name := range children { 185 childPath, err := p.Join(name) 186 if err != nil { 187 errorFn(err) 188 continue 189 } 190 191 fmt.Print("\n") 192 lsOne(ctx, config, childPath, longFormat, useSigil, true, true, errorFn) 193 } 194 } 195 } 196 197 func ls(ctx context.Context, config libkbfs.Config, args []string) (exitStatus int) { 198 flags := flag.NewFlagSet("kbfs ls", flag.ContinueOnError) 199 longFormat := flags.Bool("l", false, "List in long format.") 200 useSigil := flags.Bool("F", false, "Display sigils after each pathname.") 201 recursive := flags.Bool("R", false, "Recursively list subdirectories encountered.") 202 err := flags.Parse(args) 203 if err != nil { 204 printError("ls", err) 205 exitStatus = 1 206 return 207 } 208 209 nodePathStrs := flags.Args() 210 if len(nodePathStrs) == 0 { 211 printError("ls", errAtLeastOnePath) 212 exitStatus = 1 213 return 214 } 215 216 hasMultiple := len(nodePathStrs) > 1 217 for i, nodePathStr := range nodePathStrs { 218 p, err := fsrpc.NewPath(nodePathStr) 219 if err != nil { 220 printError("ls", err) 221 exitStatus = 1 222 continue 223 } 224 225 if i > 0 { 226 fmt.Print("\n") 227 } 228 229 lsOne(ctx, config, p, *longFormat, *useSigil, *recursive, hasMultiple, func(err error) { 230 printError("ls", err) 231 exitStatus = 1 232 }) 233 } 234 return 235 }