github.com/yasker/longhorn-engine@v0.0.0-20160621014712-6ed6cfca0729/replica/diff_disk.go (about) 1 package replica 2 3 import ( 4 "fmt" 5 "sync" 6 7 "github.com/frostschutz/go-fibmap" 8 "github.com/rancher/longhorn/types" 9 ) 10 11 type diffDisk struct { 12 rmLock sync.Mutex 13 // mapping of sector to index in the files array. a value of 0 is special meaning 14 // we don't know the location yet. 15 location []byte 16 // list of files in child, parent, grandparent, etc order. 17 // index 0 is nil and index 1 is the active write layer 18 files []types.DiffDisk 19 sectorSize int64 20 } 21 22 func (d *diffDisk) RemoveIndex(index int) error { 23 if err := d.files[index].Close(); err != nil { 24 return err 25 } 26 27 for i := 0; i < len(d.location); i++ { 28 if d.location[i] >= byte(index) { 29 // set back to unknown 30 d.location[i] = 0 31 } 32 } 33 34 d.files = append(d.files[:index], d.files[index+1:]...) 35 36 return nil 37 } 38 39 func (d *diffDisk) WriteAt(buf []byte, offset int64) (int, error) { 40 startOffset := offset % d.sectorSize 41 startCut := d.sectorSize - startOffset 42 endOffset := (int64(len(buf)) + offset) % d.sectorSize 43 44 if len(buf) == 0 { 45 return 0, nil 46 } 47 48 if startOffset == 0 && endOffset == 0 { 49 return d.fullWriteAt(buf, offset) 50 } 51 52 // single block 53 if startCut >= int64(len(buf)) { 54 return d.readModifyWrite(buf, offset) 55 } 56 57 if _, err := d.readModifyWrite(buf[0:startCut], offset); err != nil { 58 return 0, err 59 } 60 61 if _, err := d.fullWriteAt(buf[startCut:int64(len(buf))-endOffset], offset+startCut); err != nil { 62 return 0, err 63 } 64 65 if _, err := d.readModifyWrite(buf[int64(len(buf))-endOffset:], offset+int64(len(buf))-endOffset); err != nil { 66 return 0, err 67 } 68 69 return len(buf), nil 70 } 71 72 func (d *diffDisk) readModifyWrite(buf []byte, offset int64) (int, error) { 73 if len(buf) == 0 { 74 return 0, nil 75 } 76 77 d.rmLock.Lock() 78 defer d.rmLock.Unlock() 79 80 readBuf := make([]byte, d.sectorSize) 81 readOffset := (offset / d.sectorSize) * d.sectorSize 82 83 if _, err := d.fullReadAt(readBuf, readOffset); err != nil { 84 return 0, err 85 } 86 87 copy(readBuf[offset%d.sectorSize:], buf) 88 89 return d.fullWriteAt(readBuf, readOffset) 90 } 91 92 func (d *diffDisk) fullWriteAt(buf []byte, offset int64) (int, error) { 93 if int64(len(buf))%d.sectorSize != 0 || offset%d.sectorSize != 0 { 94 return 0, fmt.Errorf("Write len(%d), offset %d not a multiple of %d", len(buf), offset, d.sectorSize) 95 } 96 97 target := byte(len(d.files) - 1) 98 startSector := offset / d.sectorSize 99 sectors := int64(len(buf)) / d.sectorSize 100 101 c, err := d.files[target].WriteAt(buf, offset) 102 103 // Regardless of err mark bytes as written 104 for i := int64(0); i < sectors; i++ { 105 d.location[startSector+i] = target 106 } 107 108 return c, err 109 } 110 111 func (d *diffDisk) ReadAt(buf []byte, offset int64) (int, error) { 112 startOffset := offset % d.sectorSize 113 startCut := d.sectorSize - startOffset 114 endOffset := (int64(len(buf)) + offset) % d.sectorSize 115 116 if len(buf) == 0 { 117 return 0, nil 118 } 119 120 if startOffset == 0 && endOffset == 0 { 121 return d.fullReadAt(buf, offset) 122 } 123 124 readBuf := make([]byte, d.sectorSize) 125 if _, err := d.fullReadAt(readBuf, offset-startOffset); err != nil { 126 return 0, err 127 } 128 129 copy(buf, readBuf[startOffset:]) 130 131 if startCut >= int64(len(buf)) { 132 return len(buf), nil 133 } 134 135 if _, err := d.fullReadAt(buf[startCut:int64(len(buf))-endOffset], offset+startCut); err != nil { 136 return 0, err 137 } 138 139 if endOffset > 0 { 140 if _, err := d.fullReadAt(readBuf, offset+int64(len(buf))-endOffset); err != nil { 141 return 0, err 142 } 143 144 copy(buf[int64(len(buf))-endOffset:], readBuf[:endOffset]) 145 } 146 147 return len(buf), nil 148 } 149 150 func (d *diffDisk) fullReadAt(buf []byte, offset int64) (int, error) { 151 if int64(len(buf))%d.sectorSize != 0 || offset%d.sectorSize != 0 { 152 return 0, fmt.Errorf("Read not a multiple of %d", d.sectorSize) 153 } 154 155 if len(buf) == 0 { 156 return 0, nil 157 } 158 159 count := 0 160 sectors := int64(len(buf)) / d.sectorSize 161 readSectors := int64(1) 162 startSector := offset / d.sectorSize 163 target, err := d.lookup(startSector) 164 if err != nil { 165 return count, err 166 } 167 168 for i := int64(1); i < sectors; i++ { 169 newTarget, err := d.lookup(startSector + i) 170 if err != nil { 171 return count, err 172 } 173 174 if newTarget == target { 175 readSectors++ 176 } else { 177 c, err := d.read(target, buf, offset, i-readSectors, readSectors) 178 count += c 179 if err != nil { 180 return count, err 181 } 182 readSectors = 1 183 target = newTarget 184 } 185 } 186 187 if readSectors > 0 { 188 c, err := d.read(target, buf, offset, sectors-readSectors, readSectors) 189 count += c 190 if err != nil { 191 return count, err 192 } 193 } 194 195 return count, nil 196 } 197 198 func (d *diffDisk) read(target byte, buf []byte, offset int64, startSector int64, sectors int64) (int, error) { 199 bufStart := startSector * d.sectorSize 200 bufEnd := sectors * d.sectorSize 201 newBuf := buf[bufStart : bufStart+bufEnd] 202 return d.files[target].ReadAt(newBuf, offset+bufStart) 203 } 204 205 func (d *diffDisk) lookup(sector int64) (byte, error) { 206 if sector >= int64(len(d.location)) { 207 // We know the IO will result in EOF 208 return byte(len(d.files) - 1), nil 209 } 210 211 // small optimization 212 if int64(len(d.files)) == 2 { 213 return 1, nil 214 } 215 216 target := d.location[sector] 217 218 if target == 0 { 219 for i := len(d.files) - 1; i > 0; i-- { 220 if i == 1 { 221 // This is important that for index 1 we don't check Fiemap because it may be a base image file 222 // Also the result has to be 1 223 d.location[sector] = byte(i) 224 return byte(i), nil 225 } 226 227 e, err := fibmap.Fiemap(d.files[i].Fd(), uint64(sector*d.sectorSize), uint64(d.sectorSize), 1) 228 if err != 0 { 229 return byte(0), err 230 } 231 if len(e) > 0 { 232 d.location[sector] = byte(i) 233 return byte(i), nil 234 } 235 } 236 return byte(len(d.files) - 1), nil 237 } 238 return target, nil 239 }