This commit is contained in:
Chao Wang
2024-10-28 15:35:10 -07:00
parent 2de875e4d8
commit eb244065dc
+343 -342
View File
@@ -18,454 +18,455 @@ package restore
*/ */
import "C" import "C"
import ( import (
"errors" "errors"
"fmt" "fmt"
"sync" "sync"
"syscall" "syscall"
"unsafe" "unsafe"
) )
const batchSize = 1000 const batchSize = 1000
const bufSize = 2 * 1024 * 1024 const bufSize = 2 * 1024 * 1024
type WorkerWriter struct { type WorkerWriter struct {
writer *C.struct_scoutfs_parallel_restore_writer writer *C.struct_scoutfs_parallel_restore_writer
progressCh chan *ScoutfsParallelWriterProgress progressCh chan *ScoutfsParallelWriterProgress
fileCreated int64 fileCreated int64
devFd int devFd int
buf unsafe.Pointer buf unsafe.Pointer
wg *sync.WaitGroup wg *sync.WaitGroup
} }
type MasterWriter struct { type MasterWriter struct {
writer *C.struct_scoutfs_parallel_restore_writer writer *C.struct_scoutfs_parallel_restore_writer
progressCh chan *ScoutfsParallelWriterProgress progressCh chan *ScoutfsParallelWriterProgress
workers []*WorkerWriter workers []*WorkerWriter
wg sync.WaitGroup wg sync.WaitGroup
slice *C.struct_scoutfs_parallel_restore_slice // Add slice field slice *C.struct_scoutfs_parallel_restore_slice // Add slice field
progressWg sync.WaitGroup progressWg sync.WaitGroup
devFd int devFd int
super *C.struct_scoutfs_super_block super *C.struct_scoutfs_super_block
} }
type ScoutfsParallelWriterProgress struct { type ScoutfsParallelWriterProgress struct {
Progress *C.struct_scoutfs_parallel_restore_progress Progress *C.struct_scoutfs_parallel_restore_progress
Slice *C.struct_scoutfs_parallel_restore_slice Slice *C.struct_scoutfs_parallel_restore_slice
} }
func (m *MasterWriter) aggregateProgress() { func (m *MasterWriter) aggregateProgress() {
defer m.progressWg.Done() defer m.progressWg.Done()
for progress := range m.progressCh { for progress := range m.progressCh {
ret := C.scoutfs_parallel_restore_add_progress(m.writer, progress.Progress) ret := C.scoutfs_parallel_restore_add_progress(m.writer, progress.Progress)
if ret != 0 { if ret != 0 {
// Handle error appropriately, e.g., log it // Handle error appropriately, e.g., log it
fmt.Printf("Failed to add progress, error code: %d\n", ret) fmt.Printf("Failed to add progress, error code: %d\n", ret)
} }
if progress.Slice != nil { if progress.Slice != nil {
ret = C.scoutfs_parallel_restore_add_slice(m.writer, progress.Slice) ret = C.scoutfs_parallel_restore_add_slice(m.writer, progress.Slice)
C.free(unsafe.Pointer(progress.Slice)) C.free(unsafe.Pointer(progress.Slice))
if ret != 0 { if ret != 0 {
// Handle error appropriately, e.g., log it // Handle error appropriately, e.g., log it
fmt.Printf("Failed to add slice, error code: %d\n", ret) fmt.Printf("Failed to add slice, error code: %d\n", ret)
} }
} }
// Free the C-allocated progress structures // Free the C-allocated progress structures
C.free(unsafe.Pointer(progress.Progress)) C.free(unsafe.Pointer(progress.Progress))
} }
} }
func (m *MasterWriter) Destroy() { func (m *MasterWriter) Destroy() {
m.wg.Wait() m.wg.Wait()
close(m.progressCh) close(m.progressCh)
m.progressWg.Wait() m.progressWg.Wait()
if m.slice != nil { if m.slice != nil {
C.free(unsafe.Pointer(m.slice)) // Free slice on error C.free(unsafe.Pointer(m.slice)) // Free slice on error
} }
if m.super != nil { if m.super != nil {
C.free(unsafe.Pointer(m.super)) // Free superblock on error C.free(unsafe.Pointer(m.super)) // Free superblock on error
} }
if m.devFd != 0 { if m.devFd != 0 {
syscall.Close(m.devFd) syscall.Close(m.devFd)
} }
// Destroy master writer // Destroy master writer
C.scoutfs_parallel_restore_destroy_writer(&m.writer) C.scoutfs_parallel_restore_destroy_writer(&m.writer)
} }
func NewWriters(path string, numWriters int) (*MasterWriter, []*WorkerWriter, error) { func NewWriters(path string, numWriters int) (*MasterWriter, []*WorkerWriter, error) {
if numWriters <= 1 { if numWriters <= 1 {
return nil, nil, errors.New("number of writers must be positive") return nil, nil, errors.New("number of writers must be positive")
} }
devFd, err := syscall.Open(path, syscall.O_DIRECT|syscall.O_RDWR|syscall.O_EXCL, 0) devFd, err := syscall.Open(path, syscall.O_DIRECT|syscall.O_RDWR|syscall.O_EXCL, 0)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed to open metadata device '%s': %v", path, err) return nil, nil, fmt.Errorf("failed to open metadata device '%s': %v", path, err)
} }
var masterWriter MasterWriter var masterWriter MasterWriter
masterWriter.progressCh = make(chan *ScoutfsParallelWriterProgress, numWriters*2) masterWriter.progressCh = make(chan *ScoutfsParallelWriterProgress, numWriters*2)
masterWriter.workers = make([]*WorkerWriter, 0, numWriters-1) masterWriter.workers = make([]*WorkerWriter, 0, numWriters-1)
masterWriter.devFd = devFd masterWriter.devFd = devFd
var ret C.int var ret C.int
// Allocate aligned memory for superblock // Allocate aligned memory for superblock
var super unsafe.Pointer var super unsafe.Pointer
ret = C.posix_memalign(&super, 4096, C.SCOUTFS_BLOCK_SM_SIZE) ret = C.posix_memalign(&super, 4096, C.SCOUTFS_BLOCK_SM_SIZE)
if ret != 0 { if ret != 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, fmt.Errorf("failed to allocate aligned memory for superblock: %d", ret) return nil, nil, fmt.Errorf("failed to allocate aligned memory for superblock: %d", ret)
} }
masterWriter.super = (*C.struct_scoutfs_super_block)(super) masterWriter.super = (*C.struct_scoutfs_super_block)(super)
// Read the superblock from devFd // Read the superblock from devFd
superOffset := C.SCOUTFS_SUPER_BLKNO << C.SCOUTFS_BLOCK_SM_SHIFT superOffset := C.SCOUTFS_SUPER_BLKNO << C.SCOUTFS_BLOCK_SM_SHIFT
count, err := syscall.Pread(devFd, (*[1 << 30]byte)(super)[:C.SCOUTFS_BLOCK_SM_SIZE], int64(superOffset)) count, err := syscall.Pread(devFd, (*[1 << 30]byte)(super)[:C.SCOUTFS_BLOCK_SM_SIZE], int64(superOffset))
if err != nil { if err != nil {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, fmt.Errorf("failed to read superblock: %v", err) return nil, nil, fmt.Errorf("failed to read superblock: %v", err)
} }
if count != int(C.SCOUTFS_BLOCK_SM_SIZE) { if count != int(C.SCOUTFS_BLOCK_SM_SIZE) {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, fmt.Errorf("failed to read superblock, bytes read: %d", count) return nil, nil, fmt.Errorf("failed to read superblock, bytes read: %d", count)
} }
// Check if the superblock is valid. // Check if the superblock is valid.
if C.le64_to_cpu(masterWriter.super.flags)&C.SCOUTFS_FLAG_IS_META_BDEV == 0 { if C.le64_to_cpu(masterWriter.super.flags)&C.SCOUTFS_FLAG_IS_META_BDEV == 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, errors.New("superblock is not a metadata device") return nil, nil, errors.New("superblock is not a metadata device")
} }
// Create master writer // Create master writer
ret = C.scoutfs_parallel_restore_create_writer(&masterWriter.writer) ret = C.scoutfs_parallel_restore_create_writer(&masterWriter.writer)
if ret != 0 { if ret != 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, errors.New("failed to create master writer") return nil, nil, errors.New("failed to create master writer")
} }
ret = C.scoutfs_parallel_restore_import_super(masterWriter.writer, masterWriter.super, C.int(devFd)) ret = C.scoutfs_parallel_restore_import_super(masterWriter.writer, masterWriter.super, C.int(devFd))
if ret != 0 { if ret != 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, fmt.Errorf("failed to import superblock, error code: %d", ret) return nil, nil, fmt.Errorf("failed to import superblock, error code: %d", ret)
} }
// Initialize slices for each worker // Initialize slices for each worker
masterWriter.slice = (*C.struct_scoutfs_parallel_restore_slice)(C.malloc(C.size_t(numWriters) * masterWriter.slice = (*C.struct_scoutfs_parallel_restore_slice)(C.malloc(C.size_t(numWriters) *
C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{})))) C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{}))))
if masterWriter.slice == nil { if masterWriter.slice == nil {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, errors.New("failed to allocate slices") return nil, nil, errors.New("failed to allocate slices")
} }
ret = C.scoutfs_parallel_restore_init_slices(masterWriter.writer, ret = C.scoutfs_parallel_restore_init_slices(masterWriter.writer,
masterWriter.slice, masterWriter.slice,
C.int(numWriters)) C.int(numWriters))
if ret != 0 { if ret != 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, errors.New("failed to initialize slices") return nil, nil, errors.New("failed to initialize slices")
} }
ret = C.scoutfs_parallel_restore_add_slice(masterWriter.writer, masterWriter.slice) ret = C.scoutfs_parallel_restore_add_slice(masterWriter.writer, masterWriter.slice)
if ret != 0 { if ret != 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, errors.New("failed to add slice to master writer") return nil, nil, errors.New("failed to add slice to master writer")
} }
// Create worker writers // Create worker writers
for i := 1; i < numWriters; i++ { for i := 1; i < numWriters; i++ {
var bufPtr unsafe.Pointer var bufPtr unsafe.Pointer
if ret := C.posix_memalign(&bufPtr, 4096, bufSize); ret != 0 { if ret := C.posix_memalign(&bufPtr, 4096, bufSize); ret != 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, fmt.Errorf("failed to allocate aligned worker buffer: %d", ret) return nil, nil, fmt.Errorf("failed to allocate aligned worker buffer: %d", ret)
} }
worker := &WorkerWriter{ worker := &WorkerWriter{
progressCh: masterWriter.progressCh, progressCh: masterWriter.progressCh,
buf: bufPtr, buf: bufPtr,
wg: &masterWriter.wg, wg: &masterWriter.wg,
} }
ret = C.scoutfs_parallel_restore_create_writer(&worker.writer) ret = C.scoutfs_parallel_restore_create_writer(&worker.writer)
if ret != 0 { if ret != 0 {
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, errors.New("failed to create worker writer") return nil, nil, errors.New("failed to create worker writer")
} }
masterWriter.wg.Add(1) masterWriter.wg.Add(1)
// Use each slice for the corresponding worker // Use each slice for the corresponding worker
slice := (*C.struct_scoutfs_parallel_restore_slice)(unsafe.Pointer(uintptr(unsafe.Pointer(masterWriter.slice)) + slice := (*C.struct_scoutfs_parallel_restore_slice)(unsafe.Pointer(uintptr(unsafe.Pointer(masterWriter.slice)) +
uintptr(i)*unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{}))) uintptr(i)*unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{})))
ret = C.scoutfs_parallel_restore_add_slice(worker.writer, slice) ret = C.scoutfs_parallel_restore_add_slice(worker.writer, slice)
if ret != 0 { if ret != 0 {
C.scoutfs_parallel_restore_destroy_writer(&worker.writer) C.scoutfs_parallel_restore_destroy_writer(&worker.writer)
masterWriter.Destroy() masterWriter.Destroy()
return nil, nil, errors.New("failed to add slice to worker writer") return nil, nil, errors.New("failed to add slice to worker writer")
} }
masterWriter.workers = append(masterWriter.workers, worker) masterWriter.workers = append(masterWriter.workers, worker)
} }
go masterWriter.aggregateProgress() masterWriter.progressWg.Add(1)
go masterWriter.aggregateProgress()
return &masterWriter, masterWriter.workers, nil return &masterWriter, masterWriter.workers, nil
} }
func (w *WorkerWriter) getProgress(withSlice bool) (*ScoutfsParallelWriterProgress, error) { func (w *WorkerWriter) getProgress(withSlice bool) (*ScoutfsParallelWriterProgress, error) {
progress := (*C.struct_scoutfs_parallel_restore_progress)( progress := (*C.struct_scoutfs_parallel_restore_progress)(
C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_progress{}))), C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_progress{}))),
) )
if progress == nil { if progress == nil {
return nil, errors.New("failed to allocate memory for progress") return nil, errors.New("failed to allocate memory for progress")
} }
// Fetch the current progress from the C library // Fetch the current progress from the C library
ret := C.scoutfs_parallel_restore_get_progress(w.writer, progress) ret := C.scoutfs_parallel_restore_get_progress(w.writer, progress)
if ret != 0 { if ret != 0 {
C.free(unsafe.Pointer(progress)) C.free(unsafe.Pointer(progress))
return nil, fmt.Errorf("failed to get progress, error code: %d", ret) return nil, fmt.Errorf("failed to get progress, error code: %d", ret)
} }
var slice *C.struct_scoutfs_parallel_restore_slice var slice *C.struct_scoutfs_parallel_restore_slice
if withSlice { if withSlice {
slice = (*C.struct_scoutfs_parallel_restore_slice)( slice = (*C.struct_scoutfs_parallel_restore_slice)(
C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{}))), C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{}))),
) )
if slice == nil { if slice == nil {
C.free(unsafe.Pointer(progress)) C.free(unsafe.Pointer(progress))
return nil, errors.New("failed to allocate memory for slice") return nil, errors.New("failed to allocate memory for slice")
} }
// Optionally fetch the slice information // Optionally fetch the slice information
ret = C.scoutfs_parallel_restore_get_slice(w.writer, slice) ret = C.scoutfs_parallel_restore_get_slice(w.writer, slice)
if ret != 0 { if ret != 0 {
C.free(unsafe.Pointer(progress)) C.free(unsafe.Pointer(progress))
C.free(unsafe.Pointer(slice)) C.free(unsafe.Pointer(slice))
return nil, fmt.Errorf("failed to get slice, error code: %d", ret) return nil, fmt.Errorf("failed to get slice, error code: %d", ret)
} }
} }
return &ScoutfsParallelWriterProgress{ return &ScoutfsParallelWriterProgress{
Progress: progress, Progress: progress,
Slice: slice, Slice: slice,
}, nil }, nil
} }
// writeBuffer writes data from the buffer to the device file descriptor. // writeBuffer writes data from the buffer to the device file descriptor.
// It uses scoutfs_parallel_restore_write_buf to get data and pwrite to write it. // It uses scoutfs_parallel_restore_write_buf to get data and pwrite to write it.
func (w *WorkerWriter) writeBuffer() (int64, error) { func (w *WorkerWriter) writeBuffer() (int64, error) {
var totalWritten int64 var totalWritten int64
var count int64 var count int64
var off int64 var off int64
var ret C.int var ret C.int
// Allocate memory for off and count // Allocate memory for off and count
offPtr := (*C.off_t)(unsafe.Pointer(&off)) offPtr := (*C.off_t)(unsafe.Pointer(&off))
countPtr := (*C.size_t)(unsafe.Pointer(&count)) countPtr := (*C.size_t)(unsafe.Pointer(&count))
for { for {
ret = C.scoutfs_parallel_restore_write_buf(w.writer, w.buf, ret = C.scoutfs_parallel_restore_write_buf(w.writer, w.buf,
C.size_t(bufSize), offPtr, countPtr) C.size_t(bufSize), offPtr, countPtr)
if ret != 0 { if ret != 0 {
return totalWritten, fmt.Errorf("failed to write buffer: error code %d", ret) return totalWritten, fmt.Errorf("failed to write buffer: error code %d", ret)
} }
if count > 0 { if count > 0 {
n, err := syscall.Pwrite(w.devFd, unsafe.Slice((*byte)(w.buf), count), off) n, err := syscall.Pwrite(w.devFd, unsafe.Slice((*byte)(w.buf), count), off)
if err != nil { if err != nil {
return totalWritten, fmt.Errorf("pwrite failed: %v", err) return totalWritten, fmt.Errorf("pwrite failed: %v", err)
} }
if n != int(count) { if n != int(count) {
return totalWritten, fmt.Errorf("pwrite wrote %d bytes; expected %d", n, count) return totalWritten, fmt.Errorf("pwrite wrote %d bytes; expected %d", n, count)
} }
totalWritten += int64(n) totalWritten += int64(n)
} }
if count == 0 { if count == 0 {
break break
} }
} }
return totalWritten, nil return totalWritten, nil
} }
func (w *WorkerWriter) InsertEntry(entry *C.struct_scoutfs_parallel_restore_entry) error { func (w *WorkerWriter) InsertEntry(entry *C.struct_scoutfs_parallel_restore_entry) error {
// Add the entry using the C library // Add the entry using the C library
ret := C.scoutfs_parallel_restore_add_entry(w.writer, entry) ret := C.scoutfs_parallel_restore_add_entry(w.writer, entry)
if ret != 0 { if ret != 0 {
return fmt.Errorf("failed to add entry, error code: %d", ret) return fmt.Errorf("failed to add entry, error code: %d", ret)
} }
// Increment the fileCreated counter // Increment the fileCreated counter
w.fileCreated++ w.fileCreated++
if w.fileCreated >= batchSize { if w.fileCreated >= batchSize {
_, err := w.writeBuffer() _, err := w.writeBuffer()
if err != nil { if err != nil {
return fmt.Errorf("error writing buffers: %v", err) return fmt.Errorf("error writing buffers: %v", err)
} }
// Allocate memory for progress and slice structures // Allocate memory for progress and slice structures
progress, err := w.getProgress(false) progress, err := w.getProgress(false)
if err != nil { if err != nil {
return err return err
} }
// Send the progress update to the shared progress channel // Send the progress update to the shared progress channel
w.progressCh <- progress w.progressCh <- progress
// Reset the fileCreated counter // Reset the fileCreated counter
w.fileCreated = 0 w.fileCreated = 0
} }
return nil return nil
} }
func (w *WorkerWriter) InsertXattr(xattr *C.struct_scoutfs_parallel_restore_xattr) error { func (w *WorkerWriter) InsertXattr(xattr *C.struct_scoutfs_parallel_restore_xattr) error {
ret := C.scoutfs_parallel_restore_add_xattr(w.writer, xattr) ret := C.scoutfs_parallel_restore_add_xattr(w.writer, xattr)
if ret != 0 { if ret != 0 {
return fmt.Errorf("failed to add xattr, error code: %d", ret) return fmt.Errorf("failed to add xattr, error code: %d", ret)
} }
return nil return nil
} }
func (w *WorkerWriter) InsertInode(inode *C.struct_scoutfs_parallel_restore_inode) error { func (w *WorkerWriter) InsertInode(inode *C.struct_scoutfs_parallel_restore_inode) error {
ret := C.scoutfs_parallel_restore_add_inode(w.writer, inode) ret := C.scoutfs_parallel_restore_add_inode(w.writer, inode)
if ret != 0 { if ret != 0 {
return fmt.Errorf("failed to add inode, error code: %d", ret) return fmt.Errorf("failed to add inode, error code: %d", ret)
} }
return nil return nil
} }
// should only be called once // should only be called once
func (w *WorkerWriter) Destroy() error { func (w *WorkerWriter) Destroy() error {
defer w.wg.Done() defer w.wg.Done()
// Send final progress if there are remaining entries // Send final progress if there are remaining entries
if w.fileCreated > 0 { if w.fileCreated > 0 {
_, err := w.writeBuffer() _, err := w.writeBuffer()
if err != nil { if err != nil {
return err return err
} }
progress := &ScoutfsParallelWriterProgress{ progress := &ScoutfsParallelWriterProgress{
Progress: (*C.struct_scoutfs_parallel_restore_progress)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_progress{})))), Progress: (*C.struct_scoutfs_parallel_restore_progress)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_progress{})))),
Slice: (*C.struct_scoutfs_parallel_restore_slice)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{})))), Slice: (*C.struct_scoutfs_parallel_restore_slice)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_slice{})))),
} }
w.progressCh <- progress w.progressCh <- progress
w.fileCreated = 0 w.fileCreated = 0
} }
if w.buf != nil { if w.buf != nil {
C.free(w.buf) C.free(w.buf)
w.buf = nil w.buf = nil
} }
C.scoutfs_parallel_restore_destroy_writer(&w.writer) C.scoutfs_parallel_restore_destroy_writer(&w.writer)
return nil return nil
} }
// Add these new types and functions to the existing restore.go file // Add these new types and functions to the existing restore.go file
type FileInfo struct { type FileInfo struct {
Ino uint64 Ino uint64
Mode uint32 Mode uint32
Uid uint32 Uid uint32
Gid uint32 Gid uint32
Size uint64 Size uint64
Rdev uint64 Rdev uint64
AtimeSec int64 AtimeSec int64
AtimeNsec int64 AtimeNsec int64
MtimeSec int64 MtimeSec int64
MtimeNsec int64 MtimeNsec int64
CtimeSec int64 CtimeSec int64
CtimeNsec int64 CtimeNsec int64
NrSubdirs uint64 NrSubdirs uint64
NameBytes uint64 NameBytes uint64
IsDir bool IsDir bool
IsRegular bool IsRegular bool
} }
type XAttr struct { type XAttr struct {
Name string Name string
Value []byte Value []byte
} }
// CreateInode creates a C inode structure from FileInfo // CreateInode creates a C inode structure from FileInfo
func (w *WorkerWriter) CreateInode(info FileInfo) error { func (w *WorkerWriter) CreateInode(info FileInfo) error {
inode := (*C.struct_scoutfs_parallel_restore_inode)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_inode{})))) inode := (*C.struct_scoutfs_parallel_restore_inode)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_inode{}))))
if inode == nil { if inode == nil {
return fmt.Errorf("failed to allocate inode") return fmt.Errorf("failed to allocate inode")
} }
defer C.free(unsafe.Pointer(inode)) defer C.free(unsafe.Pointer(inode))
inode.ino = C.__u64(info.Ino) inode.ino = C.__u64(info.Ino)
inode.mode = C.__u32(info.Mode) inode.mode = C.__u32(info.Mode)
inode.uid = C.__u32(info.Uid) inode.uid = C.__u32(info.Uid)
inode.gid = C.__u32(info.Gid) inode.gid = C.__u32(info.Gid)
inode.size = C.__u64(info.Size) inode.size = C.__u64(info.Size)
inode.rdev = C.uint(info.Rdev) inode.rdev = C.uint(info.Rdev)
inode.atime.tv_sec = C.__time_t(info.AtimeSec) inode.atime.tv_sec = C.__time_t(info.AtimeSec)
inode.atime.tv_nsec = C.long(info.AtimeNsec) inode.atime.tv_nsec = C.long(info.AtimeNsec)
inode.mtime.tv_sec = C.__time_t(info.MtimeSec) inode.mtime.tv_sec = C.__time_t(info.MtimeSec)
inode.mtime.tv_nsec = C.long(info.MtimeNsec) inode.mtime.tv_nsec = C.long(info.MtimeNsec)
inode.ctime.tv_sec = C.__time_t(info.CtimeSec) inode.ctime.tv_sec = C.__time_t(info.CtimeSec)
inode.ctime.tv_nsec = C.long(info.CtimeNsec) inode.ctime.tv_nsec = C.long(info.CtimeNsec)
inode.crtime = inode.ctime inode.crtime = inode.ctime
if info.IsRegular && info.Size > 0 { if info.IsRegular && info.Size > 0 {
inode.offline = C.bool(true) inode.offline = C.bool(true)
} }
if info.IsDir { if info.IsDir {
inode.nr_subdirs = C.__u64(info.NrSubdirs) inode.nr_subdirs = C.__u64(info.NrSubdirs)
inode.total_entry_name_bytes = C.__u64(info.NameBytes) inode.total_entry_name_bytes = C.__u64(info.NameBytes)
} }
return w.InsertInode(inode) return w.InsertInode(inode)
} }
// CreateEntry creates a directory entry // CreateEntry creates a directory entry
func (w *WorkerWriter) CreateEntry(dirIno uint64, pos uint64, ino uint64, mode uint32, name string) error { func (w *WorkerWriter) CreateEntry(dirIno uint64, pos uint64, ino uint64, mode uint32, name string) error {
entryC := (*C.struct_scoutfs_parallel_restore_entry)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_entry{})) + C.size_t(len(name)))) entryC := (*C.struct_scoutfs_parallel_restore_entry)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_entry{})) + C.size_t(len(name))))
if entryC == nil { if entryC == nil {
return fmt.Errorf("failed to allocate entry") return fmt.Errorf("failed to allocate entry")
} }
defer C.free(unsafe.Pointer(entryC)) defer C.free(unsafe.Pointer(entryC))
entryC.dir_ino = C.__u64(dirIno) entryC.dir_ino = C.__u64(dirIno)
entryC.pos = C.__u64(pos) entryC.pos = C.__u64(pos)
entryC.ino = C.__u64(ino) entryC.ino = C.__u64(ino)
entryC.mode = C.__u32(mode) entryC.mode = C.__u32(mode)
entryC.name_len = C.uint(len(name)) entryC.name_len = C.uint(len(name))
entryC.name = (*C.char)(C.malloc(C.size_t(len(name)))) entryC.name = (*C.char)(C.malloc(C.size_t(len(name))))
if entryC.name == nil { if entryC.name == nil {
return fmt.Errorf("failed to allocate entry name") return fmt.Errorf("failed to allocate entry name")
} }
defer C.free(unsafe.Pointer(entryC.name)) defer C.free(unsafe.Pointer(entryC.name))
copy((*[1 << 30]byte)(unsafe.Pointer(entryC.name))[:len(name)], []byte(name)) copy((*[1 << 30]byte)(unsafe.Pointer(entryC.name))[:len(name)], []byte(name))
return w.InsertEntry(entryC) return w.InsertEntry(entryC)
} }
// CreateXAttr creates an extended attribute // CreateXAttr creates an extended attribute
func (w *WorkerWriter) CreateXAttr(ino uint64, pos uint64, xattr XAttr) error { func (w *WorkerWriter) CreateXAttr(ino uint64, pos uint64, xattr XAttr) error {
xattrC := (*C.struct_scoutfs_parallel_restore_xattr)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_xattr{})) + C.size_t(len(xattr.Name)) + C.size_t(len(xattr.Value)))) xattrC := (*C.struct_scoutfs_parallel_restore_xattr)(C.malloc(C.size_t(unsafe.Sizeof(C.struct_scoutfs_parallel_restore_xattr{})) + C.size_t(len(xattr.Name)) + C.size_t(len(xattr.Value))))
if xattrC == nil { if xattrC == nil {
return fmt.Errorf("failed to allocate xattr") return fmt.Errorf("failed to allocate xattr")
} }
defer C.free(unsafe.Pointer(xattrC)) defer C.free(unsafe.Pointer(xattrC))
xattrC.ino = C.__u64(ino) xattrC.ino = C.__u64(ino)
xattrC.pos = C.__u64(pos) xattrC.pos = C.__u64(pos)
xattrC.name_len = C.uint(len(xattr.Name)) xattrC.name_len = C.uint(len(xattr.Name))
xattrC.value_len = C.__u32(len(xattr.Value)) xattrC.value_len = C.__u32(len(xattr.Value))
xattrC.name = (*C.char)(C.malloc(C.size_t(len(xattr.Name)))) xattrC.name = (*C.char)(C.malloc(C.size_t(len(xattr.Name))))
if xattrC.name == nil { if xattrC.name == nil {
return fmt.Errorf("failed to allocate xattr name") return fmt.Errorf("failed to allocate xattr name")
} }
defer C.free(unsafe.Pointer(xattrC.name)) defer C.free(unsafe.Pointer(xattrC.name))
copy((*[1 << 30]byte)(unsafe.Pointer(xattrC.name))[:len(xattr.Name)], []byte(xattr.Name)) copy((*[1 << 30]byte)(unsafe.Pointer(xattrC.name))[:len(xattr.Name)], []byte(xattr.Name))
xattrC.value = unsafe.Pointer(&xattr.Value[0]) xattrC.value = unsafe.Pointer(&xattr.Value[0])
return w.InsertXattr(xattrC) return w.InsertXattr(xattrC)
} }