cubefs

Форк
0
/
extent.go 
583 строки · 18.5 Кб
1
// Copyright 2018 The CubeFS Authors.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
12
// implied. See the License for the specific language governing
13
// permissions and limitations under the License.
14

15
package storage
16

17
import (
18
	"encoding/binary"
19
	"fmt"
20
	"hash/crc32"
21
	"io"
22
	"math"
23
	"os"
24
	"strings"
25
	"sync"
26
	"sync/atomic"
27
	"syscall"
28
	"time"
29

30
	"github.com/cubefs/cubefs/proto"
31
	"github.com/cubefs/cubefs/util"
32
	"github.com/cubefs/cubefs/util/log"
33
)
34

35
const (
36
	ExtentOpenOpt  = os.O_CREATE | os.O_RDWR | os.O_EXCL
37
	ExtentHasClose = -1
38
	SEEK_DATA      = 3
39
	SEEK_HOLE      = 4
40
)
41

42
const (
43
	ExtentMaxSize = 1024 * 1024 * 1024 * 1024 * 4 // 4TB
44
)
45

46
type ExtentInfo struct {
47
	FileID              uint64 `json:"fileId"`
48
	Size                uint64 `json:"size"`
49
	Crc                 uint32 `json:"Crc"`
50
	IsDeleted           bool   `json:"deleted"`
51
	ModifyTime          int64  `json:"modTime"` // random write not update modify time
52
	AccessTime          int64  `json:"accessTime"`
53
	Source              string `json:"src"`
54
	SnapshotDataOff     uint64 `json:"snapSize"`
55
	SnapPreAllocDataOff uint64 `json:"snapPreAllocSize"`
56
	ApplyID             uint64 `json:"applyID"`
57
}
58

59
func (ei *ExtentInfo) TotalSize() uint64 {
60
	if ei.SnapshotDataOff > util.ExtentSize {
61
		return ei.Size + (ei.SnapshotDataOff - util.ExtentSize)
62
	}
63
	return ei.Size
64
}
65

66
func (ei *ExtentInfo) String() (m string) {
67
	source := ei.Source
68
	if source == "" {
69
		source = "none"
70
	}
71
	return fmt.Sprintf("%v_%v_%v_%v_%v_%d_%d_%d", ei.FileID, ei.Size, ei.SnapshotDataOff, ei.IsDeleted, source, ei.ModifyTime, ei.AccessTime, ei.Crc)
72
}
73

74
// SortedExtentInfos defines an array sorted by AccessTime
75
type SortedExtentInfos []*ExtentInfo
76

77
func (extInfos SortedExtentInfos) Len() int {
78
	return len(extInfos)
79
}
80

81
func (extInfos SortedExtentInfos) Less(i, j int) bool {
82
	return extInfos[i].AccessTime < extInfos[j].AccessTime
83
}
84

85
func (extInfos SortedExtentInfos) Swap(i, j int) {
86
	extInfos[i], extInfos[j] = extInfos[j], extInfos[i]
87
}
88

89
// Extent is an implementation of Extent for local regular extent file data management.
90
// This extent implementation manages all header info and data body in one single entry file.
91
// Header of extent include inode value of this extent block and Crc blocks of data blocks.
92
type Extent struct {
93
	file            *os.File
94
	filePath        string
95
	extentID        uint64
96
	modifyTime      int64
97
	accessTime      int64
98
	dataSize        int64
99
	hasClose        int32
100
	header          []byte
101
	snapshotDataOff uint64
102
	sync.Mutex
103
}
104

105
// NewExtentInCore create and returns a new extent instance.
106
func NewExtentInCore(name string, extentID uint64) *Extent {
107
	e := new(Extent)
108
	e.extentID = extentID
109
	e.filePath = name
110
	e.snapshotDataOff = util.ExtentSize
111
	return e
112
}
113

114
func (e *Extent) String() string {
115
	return fmt.Sprintf("%v_%v_%v", e.filePath, e.dataSize, e.snapshotDataOff)
116
}
117

118
func (e *Extent) GetSize() (int64, uint64) {
119
	return e.dataSize, e.snapshotDataOff
120
}
121

122
func (e *Extent) HasClosed() bool {
123
	return atomic.LoadInt32(&e.hasClose) == ExtentHasClose
124
}
125

126
// Close this extent and release FD.
127
func (e *Extent) Close() (err error) {
128
	if e.HasClosed() {
129
		return
130
	}
131
	if err = e.file.Close(); err != nil {
132
		return
133
	}
134
	return
135
}
136

137
func (e *Extent) Exist() (exsit bool) {
138
	_, err := os.Stat(e.filePath)
139
	if err != nil {
140
		return os.IsExist(err)
141
	}
142
	return true
143
}
144

145
func (e *Extent) GetFile() *os.File {
146
	return e.file
147
}
148

149
// InitToFS init extent data info filesystem. If entry file exist and overwrite is true,
150
// this operation will clear all data of exist entry file and initialize extent header data.
151
func (e *Extent) InitToFS() (err error) {
152
	if e.file, err = os.OpenFile(e.filePath, ExtentOpenOpt, 0o666); err != nil {
153
		return err
154
	}
155

156
	if IsTinyExtent(e.extentID) {
157
		e.dataSize = 0
158
		return
159
	}
160
	atomic.StoreInt64(&e.modifyTime, time.Now().Unix())
161
	atomic.StoreInt64(&e.accessTime, time.Now().Unix())
162
	e.dataSize = 0
163
	return
164
}
165

166
func (e *Extent) GetDataSize(statSize int64) (dataSize int64) {
167
	var (
168
		dataStart int64
169
		holStart  int64
170
		curOff    int64
171
		err       error
172
	)
173

174
	for {
175
		// curOff if the hold start and the data end
176
		curOff, err = e.file.Seek(holStart, SEEK_DATA)
177
		if err != nil || curOff >= util.ExtentSize || (holStart > 0 && holStart == curOff) {
178
			log.LogDebugf("GetDataSize statSize %v curOff %v dataStart %v holStart %v, err %v,path %v", statSize, curOff, dataStart, holStart, err, e.filePath)
179
			break
180
		}
181
		log.LogDebugf("GetDataSize statSize %v curOff %v dataStart %v holStart %v, err %v,path %v", statSize, curOff, dataStart, holStart, err, e.filePath)
182
		dataStart = curOff
183

184
		curOff, err = e.file.Seek(dataStart, SEEK_HOLE)
185
		if err != nil || curOff >= util.ExtentSize || dataStart == curOff {
186
			log.LogDebugf("GetDataSize statSize %v curOff %v dataStart %v holStart %v, err %v,path %v", statSize, curOff, dataStart, holStart, err, e.filePath)
187
			break
188
		}
189
		log.LogDebugf("GetDataSize statSize %v curOff %v dataStart %v holStart %v, err %v,path %v", statSize, curOff, dataStart, holStart, err, e.filePath)
190
		holStart = curOff
191
	}
192
	log.LogDebugf("GetDataSize statSize %v curOff %v dataStart %v holStart %v, err %v,path %v", statSize, curOff, dataStart, holStart, err, e.filePath)
193
	if holStart == 0 {
194
		if statSize > util.ExtentSize {
195
			return util.ExtentSize
196
		}
197
		return statSize
198
	}
199
	return holStart
200
}
201

202
// RestoreFromFS restores the entity data and status from the file stored on the filesystem.
203
func (e *Extent) RestoreFromFS() (err error) {
204
	if e.file, err = os.OpenFile(e.filePath, os.O_RDWR, 0o666); err != nil {
205
		if strings.Contains(err.Error(), syscall.ENOENT.Error()) {
206
			err = ExtentNotFoundError
207
		}
208
		return err
209
	}
210
	var info os.FileInfo
211
	if info, err = e.file.Stat(); err != nil {
212
		err = fmt.Errorf("stat file %v: %v", e.file.Name(), err)
213
		return
214
	}
215

216
	if IsTinyExtent(e.extentID) {
217
		watermark := info.Size()
218
		if watermark%util.PageSize != 0 {
219
			watermark = watermark + (util.PageSize - watermark%util.PageSize)
220
		}
221
		e.dataSize = watermark
222
		return
223
	}
224

225
	e.dataSize = e.GetDataSize(info.Size())
226
	e.snapshotDataOff = util.ExtentSize
227
	if info.Size() > util.ExtentSize {
228
		e.snapshotDataOff = uint64(info.Size())
229
	}
230

231
	atomic.StoreInt64(&e.modifyTime, info.ModTime().Unix())
232

233
	ts := info.Sys().(*syscall.Stat_t)
234
	atomic.StoreInt64(&e.accessTime, time.Unix(int64(ts.Atim.Sec), int64(ts.Atim.Nsec)).Unix())
235
	return
236
}
237

238
// Size returns length of the extent (not including the header).
239
func (e *Extent) Size() (size int64) {
240
	return e.dataSize
241
}
242

243
// ModifyTime returns the time when this extent was modified recently.
244
func (e *Extent) ModifyTime() int64 {
245
	return atomic.LoadInt64(&e.modifyTime)
246
}
247

248
func IsRandomWrite(writeType int) bool {
249
	return writeType == RandomWriteType
250
}
251

252
func IsAppendWrite(writeType int) bool {
253
	return writeType == AppendWriteType
254
}
255

256
func IsAppendRandomWrite(writeType int) bool {
257
	return writeType == AppendRandomWriteType
258
}
259

260
// WriteTiny performs write on a tiny extent.
261
func (e *Extent) WriteTiny(data []byte, offset, size int64, crc uint32, writeType int, isSync bool) (err error) {
262
	e.Lock()
263
	defer e.Unlock()
264
	index := offset + size
265
	if index >= ExtentMaxSize {
266
		return ExtentIsFullError
267
	}
268

269
	if IsAppendWrite(writeType) && offset != e.dataSize {
270
		return ParameterMismatchError
271
	}
272

273
	if _, err = e.file.WriteAt(data[:size], int64(offset)); err != nil {
274
		return
275
	}
276
	if isSync {
277
		if err = e.file.Sync(); err != nil {
278
			return
279
		}
280
	}
281

282
	if !IsAppendWrite(writeType) {
283
		return
284
	}
285
	if index%util.PageSize != 0 {
286
		index = index + (util.PageSize - index%util.PageSize)
287
	}
288
	e.dataSize = index
289

290
	return
291
}
292

293
// Write writes data to an extent.
294
func (e *Extent) Write(data []byte, offset, size int64, crc uint32, writeType int, isSync bool, crcFunc UpdateCrcFunc, ei *ExtentInfo) (status uint8, err error) {
295
	log.LogDebugf("action[Extent.Write] path %v offset %v size %v writeType %v", e.filePath, offset, size, writeType)
296
	status = proto.OpOk
297
	if IsTinyExtent(e.extentID) {
298
		err = e.WriteTiny(data, offset, size, crc, writeType, isSync)
299
		return
300
	}
301

302
	if err = e.checkWriteOffsetAndSize(writeType, offset, size); err != nil {
303
		log.LogErrorf("action[Extent.Write] checkWriteOffsetAndSize offset %v size %v writeType %v err %v",
304
			offset, size, writeType, err)
305
		err = newParameterError("extent current size=%d write offset=%d write size=%d", e.dataSize, offset, size)
306
		log.LogInfof("action[Extent.Write] newParameterError path %v offset %v size %v writeType %v err %v", e.filePath,
307
			offset, size, writeType, err)
308
		status = proto.OpTryOtherExtent
309
		return
310
	}
311

312
	log.LogDebugf("action[Extent.Write] path %v offset %v size %v writeType %v", e.filePath, offset, size, writeType)
313
	// Check if extent file size matches the write offset just in case
314
	// multiple clients are writing concurrently.
315
	e.Lock()
316
	defer e.Unlock()
317
	log.LogDebugf("action[Extent.Write] offset %v size %v writeType %v path %v", offset, size, writeType, e.filePath)
318
	if IsAppendWrite(writeType) && e.dataSize != offset {
319
		err = newParameterError("extent current size=%d write offset=%d write size=%d", e.dataSize, offset, size)
320
		log.LogInfof("action[Extent.Write] newParameterError path %v offset %v size %v writeType %v err %v", e.filePath,
321
			offset, size, writeType, err)
322
		status = proto.OpTryOtherExtent
323
		return
324
	}
325
	if IsAppendRandomWrite(writeType) {
326
		if e.snapshotDataOff <= util.ExtentSize {
327
			log.LogInfof("action[Extent.Write] truncate extent %v offset %v size %v writeType %v truncate err %v", e, offset, size, writeType, err)
328
			if err = e.file.Truncate(util.ExtentSize); err != nil {
329
				log.LogErrorf("action[Extent.Write] offset %v size %v writeType %v truncate err %v", offset, size, writeType, err)
330
				return
331
			}
332
		}
333
	}
334
	if _, err = e.file.WriteAt(data[:size], int64(offset)); err != nil {
335
		log.LogErrorf("action[Extent.Write] offset %v size %v writeType %v err %v", offset, size, writeType, err)
336
		return
337
	}
338

339
	blockNo := offset / util.BlockSize
340
	offsetInBlock := offset % util.BlockSize
341
	defer func() {
342
		log.LogDebugf("action[Extent.Write] offset %v size %v writeType %v path %v", offset, size, writeType, e.filePath)
343
		if IsAppendWrite(writeType) {
344
			atomic.StoreInt64(&e.modifyTime, time.Now().Unix())
345
			e.dataSize = int64(math.Max(float64(e.dataSize), float64(offset+size)))
346
			log.LogDebugf("action[Extent.Write] e %v offset %v size %v writeType %v", e, offset, size, writeType)
347
		} else if IsAppendRandomWrite(writeType) {
348
			atomic.StoreInt64(&e.modifyTime, time.Now().Unix())
349
			e.snapshotDataOff = uint64(math.Max(float64(e.snapshotDataOff), float64(offset+size)))
350
		}
351
		log.LogDebugf("action[Extent.Write] offset %v size %v writeType %v dataSize %v snapshotDataOff %v",
352
			offset, size, writeType, e.dataSize, e.snapshotDataOff)
353
	}()
354

355
	if isSync {
356
		if err = e.file.Sync(); err != nil {
357
			log.LogDebugf("action[Extent.Write] offset %v size %v writeType %v err %v",
358
				offset, size, writeType, err)
359
			return
360
		}
361
	}
362
	if offsetInBlock == 0 && size == util.BlockSize {
363
		err = crcFunc(e, int(blockNo), crc)
364
		log.LogDebugf("action[Extent.Write] offset %v size %v writeType %v err %v", offset, size, writeType, err)
365
		return
366
	}
367

368
	if offsetInBlock+size <= util.BlockSize {
369
		err = crcFunc(e, int(blockNo), 0)
370
		log.LogDebugf("action[Extent.Write]  offset %v size %v writeType %v err %v", offset, size, writeType, err)
371
		return
372
	}
373
	log.LogDebugf("action[Extent.Write] offset %v size %v writeType %v", offset, size, writeType)
374
	if err = crcFunc(e, int(blockNo), 0); err == nil {
375
		err = crcFunc(e, int(blockNo+1), 0)
376
	}
377
	return
378
}
379

380
// Read reads data from an extent.
381
func (e *Extent) Read(data []byte, offset, size int64, isRepairRead bool) (crc uint32, err error) {
382
	log.LogDebugf("action[Extent.read] offset %v size %v extent %v", offset, size, e)
383
	if IsTinyExtent(e.extentID) {
384
		return e.ReadTiny(data, offset, size, isRepairRead)
385
	}
386

387
	if err = e.checkReadOffsetAndSize(offset, size); err != nil {
388
		log.LogErrorf("action[Extent.Read] offset %d size %d err %v", offset, size, err)
389
		return
390
	}
391

392
	var rSize int
393
	if rSize, err = e.file.ReadAt(data[:size], offset); err != nil {
394
		log.LogErrorf("action[Extent.Read] offset %v size %v err %v realsize %v", offset, size, err, rSize)
395
		return
396
	}
397
	crc = crc32.ChecksumIEEE(data)
398
	return
399
}
400

401
// ReadTiny read data from a tiny extent.
402
func (e *Extent) ReadTiny(data []byte, offset, size int64, isRepairRead bool) (crc uint32, err error) {
403
	_, err = e.file.ReadAt(data[:size], offset)
404
	if isRepairRead && err == io.EOF {
405
		err = nil
406
	}
407
	crc = crc32.ChecksumIEEE(data[:size])
408
	return
409
}
410

411
func (e *Extent) checkReadOffsetAndSize(offset, size int64) error {
412
	if (e.snapshotDataOff == util.ExtentSize && offset > e.Size()) ||
413
		(e.snapshotDataOff > util.ExtentSize && uint64(offset) > e.snapshotDataOff) {
414
		return newParameterError("offset=%d size=%d snapshotDataOff=%d", offset, size, e.snapshotDataOff)
415
	}
416
	return nil
417
}
418

419
func (e *Extent) checkWriteOffsetAndSize(writeType int, offset, size int64) error {
420
	err := newParameterError("writeType=%d offset=%d size=%d", writeType, offset, size)
421
	if IsAppendWrite(writeType) {
422
		if size == 0 || size > util.BlockSize ||
423
			offset+size > util.ExtentSize || offset >= util.ExtentSize {
424
			return err
425
		}
426
	} else if IsAppendRandomWrite(writeType) {
427
		log.LogDebugf("action[checkOffsetAndSize] offset %v size %v", offset, size)
428
		if offset < util.ExtentSize || size == 0 {
429
			return err
430
		}
431
	}
432
	return nil
433
}
434

435
// Flush synchronizes data to the disk.
436
func (e *Extent) Flush() (err error) {
437
	err = e.file.Sync()
438
	return
439
}
440

441
func (e *Extent) GetCrc(blockNo int64) uint32 {
442
	if int64(len(e.header)) < (blockNo+1)*util.PerBlockCrcSize {
443
		return 0
444
	}
445
	return binary.BigEndian.Uint32(e.header[blockNo*util.PerBlockCrcSize : (blockNo+1)*util.PerBlockCrcSize])
446
}
447

448
func (e *Extent) autoComputeExtentCrc(crcFunc UpdateCrcFunc) (crc uint32, err error) {
449
	var blockCnt int
450
	extSize := e.Size()
451
	if e.snapshotDataOff > util.ExtentSize {
452
		extSize = int64(e.snapshotDataOff)
453
	}
454
	blockCnt = int(extSize / util.BlockSize)
455
	if extSize%util.BlockSize != 0 {
456
		blockCnt += 1
457
	}
458
	log.LogDebugf("autoComputeExtentCrc. path %v extent %v extent size %v,blockCnt %v", e.filePath, e.extentID, extSize, blockCnt)
459
	crcData := make([]byte, blockCnt*util.PerBlockCrcSize)
460
	for blockNo := 0; blockNo < blockCnt; blockNo++ {
461
		blockCrc := binary.BigEndian.Uint32(e.header[blockNo*util.PerBlockCrcSize : (blockNo+1)*util.PerBlockCrcSize])
462
		if blockCrc != 0 {
463
			binary.BigEndian.PutUint32(crcData[blockNo*util.PerBlockCrcSize:(blockNo+1)*util.PerBlockCrcSize], blockCrc)
464
			continue
465
		}
466
		bdata := make([]byte, util.BlockSize)
467
		offset := int64(blockNo * util.BlockSize)
468
		readN, err := e.file.ReadAt(bdata[:util.BlockSize], offset)
469
		if readN == 0 && err != nil {
470
			log.LogErrorf("autoComputeExtentCrc. path %v extent %v blockNo %v, readN %v err %v", e.filePath, e.extentID, blockNo, readN, err)
471
			break
472
		}
473
		blockCrc = crc32.ChecksumIEEE(bdata[:readN])
474
		err = crcFunc(e, blockNo, blockCrc)
475
		if err != nil {
476
			log.LogErrorf("autoComputeExtentCrc. path %v extent %v blockNo %v, err %v", e.filePath, e.extentID, blockNo, err)
477
			return 0, nil
478
		}
479
		log.LogDebugf("autoComputeExtentCrc. path %v extent %v blockCrc %v,blockNo %v", e.filePath, e.extentID, blockCrc, blockNo)
480
		binary.BigEndian.PutUint32(crcData[blockNo*util.PerBlockCrcSize:(blockNo+1)*util.PerBlockCrcSize], blockCrc)
481
	}
482
	crc = crc32.ChecksumIEEE(crcData)
483
	log.LogDebugf("autoComputeExtentCrc. path %v extent %v crc %v", e.filePath, e.extentID, crc)
484
	return crc, err
485
}
486

487
// DeleteTiny deletes a tiny extent.
488
func (e *Extent) punchDelete(offset, size int64) (hasDelete bool, err error) {
489
	log.LogDebugf("punchDelete extent %v offset %v, size %v", e, offset, size)
490
	if int(offset)%util.PageSize != 0 {
491
		return false, ParameterMismatchError
492
	}
493
	if int(size)%util.PageSize != 0 {
494
		size += int64(util.PageSize - int(size)%util.PageSize)
495
	}
496

497
	newOffset, err := e.file.Seek(offset, SEEK_DATA)
498
	if err != nil {
499
		if strings.Contains(err.Error(), syscall.ENXIO.Error()) {
500
			return true, nil
501
		}
502
		return false, err
503
	}
504
	if newOffset-offset >= size {
505
		return true, nil
506
	}
507
	log.LogDebugf("punchDelete offset %v size %v", offset, size)
508
	err = fallocate(int(e.file.Fd()), util.FallocFLPunchHole|util.FallocFLKeepSize, offset, size)
509
	return
510
}
511

512
func (e *Extent) getRealBlockCnt() (blockNum int64) {
513
	stat := new(syscall.Stat_t)
514
	syscall.Stat(e.filePath, stat)
515
	return stat.Blocks
516
}
517

518
func (e *Extent) TinyExtentRecover(data []byte, offset, size int64, crc uint32, isEmptyPacket bool) (err error) {
519
	e.Lock()
520
	defer e.Unlock()
521
	if !IsTinyExtent(e.extentID) {
522
		return ParameterMismatchError
523
	}
524
	if offset%util.PageSize != 0 || offset != e.dataSize {
525
		return fmt.Errorf("error empty packet on (%v) offset(%v) size(%v)"+
526
			" isEmptyPacket(%v)  e.dataSize(%v)", e.file.Name(), offset, size, isEmptyPacket, e.dataSize)
527
	}
528
	log.LogDebugf("before file (%v) getRealBlockNo (%v) isEmptyPacket(%v)"+
529
		"offset(%v) size(%v) e.datasize(%v)", e.filePath, e.getRealBlockCnt(), isEmptyPacket, offset, size, e.dataSize)
530
	if isEmptyPacket {
531
		var finfo os.FileInfo
532
		finfo, err = e.file.Stat()
533
		if err != nil {
534
			return err
535
		}
536
		if offset < finfo.Size() {
537
			return fmt.Errorf("error empty packet on (%v) offset(%v) size(%v)"+
538
				" isEmptyPacket(%v) filesize(%v) e.dataSize(%v)", e.file.Name(), offset, size, isEmptyPacket, finfo.Size(), e.dataSize)
539
		}
540
		if err = syscall.Ftruncate(int(e.file.Fd()), offset+size); err != nil {
541
			return err
542
		}
543
		err = fallocate(int(e.file.Fd()), util.FallocFLPunchHole|util.FallocFLKeepSize, offset, size)
544
	} else {
545
		_, err = e.file.WriteAt(data[:size], int64(offset))
546
	}
547
	if err != nil {
548
		return
549
	}
550
	watermark := offset + size
551
	if watermark%util.PageSize != 0 {
552
		watermark = watermark + (util.PageSize - watermark%util.PageSize)
553
	}
554
	e.dataSize = watermark
555
	log.LogDebugf("after file (%v) getRealBlockNo (%v) isEmptyPacket(%v)"+
556
		"offset(%v) size(%v) e.datasize(%v)", e.filePath, e.getRealBlockCnt(), isEmptyPacket, offset, size, e.dataSize)
557

558
	return
559
}
560

561
func (e *Extent) tinyExtentAvaliOffset(offset int64) (newOffset, newEnd int64, err error) {
562
	e.Lock()
563
	defer e.Unlock()
564
	newOffset, err = e.file.Seek(int64(offset), SEEK_DATA)
565
	if err != nil {
566
		return
567
	}
568
	newEnd, err = e.file.Seek(int64(newOffset), SEEK_HOLE)
569
	if err != nil {
570
		return
571
	}
572
	if newOffset-offset > util.BlockSize {
573
		newOffset = offset + util.BlockSize
574
	}
575
	if newEnd-newOffset > util.BlockSize {
576
		newEnd = newOffset + util.BlockSize
577
	}
578
	if newEnd < newOffset {
579
		err = fmt.Errorf("unavali TinyExtentAvaliOffset on SEEK_DATA or SEEK_HOLE   (%v) offset(%v) "+
580
			"newEnd(%v) newOffset(%v)", e.extentID, offset, newEnd, newOffset)
581
	}
582
	return
583
}
584

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.