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 }