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  }