github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/kbfs/kbfstool/write.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  	"io"
    11  	"os"
    12  
    13  	"github.com/keybase/client/go/kbfs/fsrpc"
    14  	"github.com/keybase/client/go/kbfs/idutil"
    15  	"github.com/keybase/client/go/kbfs/libkbfs"
    16  	"golang.org/x/net/context"
    17  )
    18  
    19  type nodeWriter struct {
    20  	ctx     context.Context
    21  	kbfsOps libkbfs.KBFSOps
    22  	node    libkbfs.Node
    23  	off     int64
    24  	verbose bool
    25  }
    26  
    27  var _ io.Writer = (*nodeWriter)(nil)
    28  
    29  func (nw *nodeWriter) Write(p []byte) (n int, err error) {
    30  	if nw.verbose {
    31  		fmt.Fprintf(os.Stderr, "Writing %s at offset %d\n", byteCountStr(len(p)), nw.off)
    32  	}
    33  	err = nw.kbfsOps.Write(nw.ctx, nw.node, p, nw.off)
    34  	if err == nil {
    35  		n = len(p)
    36  		nw.off += int64(n)
    37  	}
    38  	return
    39  }
    40  
    41  func writeHelper(ctx context.Context, config libkbfs.Config, args []string) (err error) {
    42  	flags := flag.NewFlagSet("kbfs write", flag.ContinueOnError)
    43  	append := flags.Bool("a", false, "Append to an existing file instead of truncating it.")
    44  	verbose := flags.Bool("v", false, "Print extra status output.")
    45  	err = flags.Parse(args)
    46  	if err != nil {
    47  		return err
    48  	}
    49  
    50  	if flags.NArg() != 1 {
    51  		return errExactlyOnePath
    52  	}
    53  
    54  	filePathStr := flags.Arg(0)
    55  
    56  	defer func() {
    57  		if err != nil {
    58  			if _, ok := err.(cannotWriteErr); !ok {
    59  				err = cannotWriteErr{filePathStr, err}
    60  			}
    61  		}
    62  	}()
    63  
    64  	p, err := fsrpc.NewPath(filePathStr)
    65  	if err != nil {
    66  		return
    67  	}
    68  
    69  	if p.PathType != fsrpc.TLFPathType {
    70  		err = cannotWriteErr{filePathStr, nil}
    71  		return
    72  	}
    73  
    74  	dir, filename, err := p.DirAndBasename()
    75  	if err != nil {
    76  		return
    77  	}
    78  
    79  	if dir.PathType != fsrpc.TLFPathType {
    80  		err = cannotWriteErr{filePathStr, nil}
    81  		return
    82  	}
    83  
    84  	parentNode, err := dir.GetDirNode(ctx, config)
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	kbfsOps := config.KBFSOps()
    90  
    91  	noSuchFileErr := idutil.NoSuchNameError{Name: filename}
    92  
    93  	// The operations below are racy, but that is inherent to a
    94  	// distributed FS.
    95  
    96  	filenamePPS := parentNode.ChildName(filename)
    97  	fileNode, de, err := kbfsOps.Lookup(ctx, parentNode, filenamePPS)
    98  	if err != nil && err != noSuchFileErr {
    99  		return err
   100  	}
   101  
   102  	needSync := false
   103  	var off int64
   104  
   105  	if err == noSuchFileErr {
   106  		if *verbose {
   107  			fmt.Fprintf(os.Stderr, "Creating %s\n", p)
   108  		}
   109  		fileNode, _, err = kbfsOps.CreateFile(
   110  			ctx, parentNode, filenamePPS, false, libkbfs.NoExcl)
   111  		if err != nil {
   112  			return err
   113  		}
   114  	} else {
   115  		if *append {
   116  			if *verbose {
   117  				fmt.Fprintf(os.Stderr, "Appending to %s\n", p)
   118  			}
   119  
   120  			off = int64(de.Size)
   121  		} else {
   122  			if *verbose {
   123  				fmt.Fprintf(os.Stderr, "Truncating %s\n", p)
   124  			}
   125  			err = kbfsOps.Truncate(ctx, fileNode, 0)
   126  			if err != nil {
   127  				return err
   128  			}
   129  
   130  			needSync = true
   131  		}
   132  	}
   133  
   134  	nw := nodeWriter{
   135  		ctx:     ctx,
   136  		kbfsOps: kbfsOps,
   137  		node:    fileNode,
   138  		off:     off,
   139  		verbose: *verbose,
   140  	}
   141  
   142  	written, err := io.Copy(&nw, os.Stdin)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	if written > 0 {
   148  		needSync = true
   149  	}
   150  
   151  	if needSync {
   152  		if *verbose {
   153  			fmt.Fprintf(os.Stderr, "Syncing %s\n", p)
   154  		}
   155  		err := kbfsOps.SyncAll(ctx, fileNode.GetFolderBranch())
   156  		if err != nil {
   157  			return err
   158  		}
   159  	}
   160  
   161  	return nil
   162  }
   163  
   164  func write(ctx context.Context, config libkbfs.Config, args []string) (exitStatus int) {
   165  	err := writeHelper(ctx, config, args)
   166  	if err != nil {
   167  		printError("write", err)
   168  		exitStatus = 1
   169  	}
   170  	return
   171  }