github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/unixfs/io/dagmodifier.go (about) 1 package io 2 3 import ( 4 "bytes" 5 "errors" 6 7 proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto" 8 9 chunk "github.com/jbenet/go-ipfs/importer/chunk" 10 mdag "github.com/jbenet/go-ipfs/merkledag" 11 ft "github.com/jbenet/go-ipfs/unixfs" 12 ftpb "github.com/jbenet/go-ipfs/unixfs/pb" 13 u "github.com/jbenet/go-ipfs/util" 14 ) 15 16 var log = u.Logger("dagio") 17 18 // DagModifier is the only struct licensed and able to correctly 19 // perform surgery on a DAG 'file' 20 // Dear god, please rename this to something more pleasant 21 type DagModifier struct { 22 dagserv mdag.DAGService 23 curNode *mdag.Node 24 25 pbdata *ftpb.Data 26 splitter chunk.BlockSplitter 27 } 28 29 func NewDagModifier(from *mdag.Node, serv mdag.DAGService, spl chunk.BlockSplitter) (*DagModifier, error) { 30 pbd, err := ft.FromBytes(from.Data) 31 if err != nil { 32 return nil, err 33 } 34 35 return &DagModifier{ 36 curNode: from.Copy(), 37 dagserv: serv, 38 pbdata: pbd, 39 splitter: spl, 40 }, nil 41 } 42 43 // WriteAt will modify a dag file in place 44 // NOTE: it currently assumes only a single level of indirection 45 func (dm *DagModifier) WriteAt(b []byte, offset uint64) (int, error) { 46 47 // Check bounds 48 if dm.pbdata.GetFilesize() < offset { 49 return 0, errors.New("Attempted to perform write starting past end of file") 50 } 51 52 // First need to find where we are writing at 53 end := uint64(len(b)) + offset 54 55 // This shouldnt be necessary if we do subblocks sizes properly 56 newsize := dm.pbdata.GetFilesize() 57 if end > dm.pbdata.GetFilesize() { 58 newsize = end 59 } 60 zeroblocklen := uint64(len(dm.pbdata.Data)) 61 origlen := len(b) 62 63 if end <= zeroblocklen { 64 log.Debug("Writing into zero block") 65 // Replacing zeroeth data block (embedded in the root node) 66 //TODO: check chunking here 67 copy(dm.pbdata.Data[offset:], b) 68 return len(b), nil 69 } 70 71 // Find where write should start 72 var traversed uint64 73 startsubblk := len(dm.pbdata.Blocksizes) 74 if offset < zeroblocklen { 75 dm.pbdata.Data = dm.pbdata.Data[:offset] 76 startsubblk = 0 77 } else { 78 traversed = uint64(zeroblocklen) 79 for i, size := range dm.pbdata.Blocksizes { 80 if uint64(offset) < traversed+size { 81 log.Debugf("Starting mod at block %d. [%d < %d + %d]", i, offset, traversed, size) 82 // Here is where we start 83 startsubblk = i 84 lnk := dm.curNode.Links[i] 85 node, err := dm.dagserv.Get(u.Key(lnk.Hash)) 86 if err != nil { 87 return 0, err 88 } 89 data, err := ft.UnwrapData(node.Data) 90 if err != nil { 91 return 0, err 92 } 93 94 // We have to rewrite the data before our write in this block. 95 b = append(data[:offset-traversed], b...) 96 break 97 } 98 traversed += size 99 } 100 if startsubblk == len(dm.pbdata.Blocksizes) { 101 // TODO: Im not sure if theres any case that isnt being handled here. 102 // leaving this note here as a future reference in case something breaks 103 } 104 } 105 106 // Find blocks that need to be overwritten 107 var changed []int 108 mid := -1 109 var midoff uint64 110 for i, size := range dm.pbdata.Blocksizes[startsubblk:] { 111 if end > traversed { 112 changed = append(changed, i+startsubblk) 113 } else { 114 break 115 } 116 traversed += size 117 if end < traversed { 118 mid = i + startsubblk 119 midoff = end - (traversed - size) 120 break 121 } 122 } 123 124 // If our write starts in the middle of a block... 125 var midlnk *mdag.Link 126 if mid >= 0 { 127 midlnk = dm.curNode.Links[mid] 128 midnode, err := dm.dagserv.Get(u.Key(midlnk.Hash)) 129 if err != nil { 130 return 0, err 131 } 132 133 // NOTE: this may have to be changed later when we have multiple 134 // layers of indirection 135 data, err := ft.UnwrapData(midnode.Data) 136 if err != nil { 137 return 0, err 138 } 139 b = append(b, data[midoff:]...) 140 } 141 142 // Generate new sub-blocks, and sizes 143 subblocks := splitBytes(b, dm.splitter) 144 var links []*mdag.Link 145 var sizes []uint64 146 for _, sb := range subblocks { 147 n := &mdag.Node{Data: ft.WrapData(sb)} 148 _, err := dm.dagserv.Add(n) 149 if err != nil { 150 log.Errorf("Failed adding node to DAG service: %s", err) 151 return 0, err 152 } 153 lnk, err := mdag.MakeLink(n) 154 if err != nil { 155 return 0, err 156 } 157 links = append(links, lnk) 158 sizes = append(sizes, uint64(len(sb))) 159 } 160 161 // This is disgusting (and can be rewritten if performance demands) 162 if len(changed) > 0 { 163 sechalflink := append(links, dm.curNode.Links[changed[len(changed)-1]+1:]...) 164 dm.curNode.Links = append(dm.curNode.Links[:changed[0]], sechalflink...) 165 sechalfblks := append(sizes, dm.pbdata.Blocksizes[changed[len(changed)-1]+1:]...) 166 dm.pbdata.Blocksizes = append(dm.pbdata.Blocksizes[:changed[0]], sechalfblks...) 167 } else { 168 dm.curNode.Links = append(dm.curNode.Links, links...) 169 dm.pbdata.Blocksizes = append(dm.pbdata.Blocksizes, sizes...) 170 } 171 dm.pbdata.Filesize = proto.Uint64(newsize) 172 173 return origlen, nil 174 } 175 176 func (dm *DagModifier) Size() uint64 { 177 if dm == nil { 178 return 0 179 } 180 return dm.pbdata.GetFilesize() 181 } 182 183 // splitBytes uses a splitterFunc to turn a large array of bytes 184 // into many smaller arrays of bytes 185 func splitBytes(b []byte, spl chunk.BlockSplitter) [][]byte { 186 out := spl.Split(bytes.NewReader(b)) 187 var arr [][]byte 188 for blk := range out { 189 arr = append(arr, blk) 190 } 191 return arr 192 } 193 194 // GetNode gets the modified DAG Node 195 func (dm *DagModifier) GetNode() (*mdag.Node, error) { 196 b, err := proto.Marshal(dm.pbdata) 197 if err != nil { 198 return nil, err 199 } 200 dm.curNode.Data = b 201 return dm.curNode.Copy(), nil 202 }