- Fix mount point property race condition causing segmentation fault failures

This commit is contained in:
Faruk Kasumovic 2020-02-10 11:12:46 +01:00
parent 4dcea86954
commit 7c7ef79611
3 changed files with 62 additions and 9 deletions

View File

@ -22,6 +22,9 @@ func Test(t *testing.T) {
zfsTestDatasetCreate(t) zfsTestDatasetCreate(t)
zfsTestDatasetOpen(t) zfsTestDatasetOpen(t)
// zfsTestMountPointConcurrency(t)
// time.Sleep(15 * time.Second)
zfsTestDatasetSnapshot(t) zfsTestDatasetSnapshot(t)
zfsTestDatasetOpenAll(t) zfsTestDatasetOpenAll(t)
zfsTestDatasetSetProperty(t) zfsTestDatasetSetProperty(t)

34
zfs.go
View File

@ -125,13 +125,12 @@ func DatasetOpenSingle(path string) (d Dataset, err error) {
if d.list == nil || d.list.zh == nil { if d.list == nil || d.list.zh == nil {
err = LastError() err = LastError()
if err == nil { if err == nil {
err = fmt.Errorf("dataset not found.") err = fmt.Errorf("dataset not found")
} }
err = fmt.Errorf("%s - %s", err.Error(), path) err = fmt.Errorf("%s - %s", err.Error(), path)
return return
} }
d.Type = DatasetType(C.dataset_type(d.list)) d.Type = DatasetType(C.dataset_type(d.list))
d.Properties = make(map[Prop]Property)
err = d.ReloadProperties() err = d.ReloadProperties()
if err != nil { if err != nil {
return return
@ -317,11 +316,17 @@ func (d *Dataset) ReloadProperties() (err error) {
return return
} }
d.Properties = make(map[Prop]Property) d.Properties = make(map[Prop]Property)
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
C.zfs_refresh_properties(d.list.zh) C.zfs_refresh_properties(d.list.zh)
for prop := DatasetPropType; prop < DatasetNumProps; prop++ { for prop := DatasetPropType; prop < DatasetNumProps; prop++ {
plist := C.read_dataset_property(d.list, C.int(prop)) var plist *C.property_list_t
if prop == DatasetPropMounted || prop == DatasetPropMountpoint {
// prevent zfs mountpoint race conditions
Global.Mtx.Lock()
plist = C.read_dataset_property(d.list, C.int(prop))
Global.Mtx.Unlock()
} else {
plist = C.read_dataset_property(d.list, C.int(prop))
}
if plist == nil { if plist == nil {
continue continue
} }
@ -339,8 +344,11 @@ func (d *Dataset) GetProperty(p Prop) (prop Property, err error) {
err = errors.New(msgDatasetIsNil) err = errors.New(msgDatasetIsNil)
return return
} }
Global.Mtx.Lock() if p == DatasetPropMounted || p == DatasetPropMountpoint {
defer Global.Mtx.Unlock() // prevent zfs mountpoint race conditions
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
}
plist := C.read_dataset_property(d.list, C.int(p)) plist := C.read_dataset_property(d.list, C.int(p))
if plist == nil { if plist == nil {
err = LastError() err = LastError()
@ -381,6 +389,11 @@ func (d *Dataset) SetProperty(p Prop, value string) (err error) {
return return
} }
csValue := C.CString(value) csValue := C.CString(value)
if p == DatasetPropMounted || p == DatasetPropMountpoint {
// prevent zfs mountpoint race conditions
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
}
errcode := C.dataset_prop_set(d.list, C.zfs_prop_t(p), csValue) errcode := C.dataset_prop_set(d.list, C.zfs_prop_t(p), csValue)
C.free(unsafe.Pointer(csValue)) C.free(unsafe.Pointer(csValue))
if errcode != 0 { if errcode != 0 {
@ -388,9 +401,14 @@ func (d *Dataset) SetProperty(p Prop, value string) (err error) {
return return
} }
// Update Properties member with change made // Update Properties member with change made
if _, err = d.GetProperty(p); err != nil { plist := C.read_dataset_property(d.list, C.int(p))
if plist == nil {
err = LastError()
return return
} }
defer C.free_properties(plist)
d.Properties[p] = Property{Value: C.GoString(&(*plist).value[0]),
Source: C.GoString(&(*plist).source[0])}
return return
} }

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"testing" "testing"
"github.com/bicomsystems/go-libzfs" zfs "github.com/bicomsystems/go-libzfs"
) )
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
@ -203,6 +203,38 @@ func zfsTestDatasetDestroy(t *testing.T) {
print("PASS\n\n") print("PASS\n\n")
} }
func zfsTestMountPointConcurrency(t *testing.T) {
println("TEST DATASET MountPointConcurrency( ", TSTDatasetPath, " ) ... ")
d, err := zfs.DatasetOpen(TSTDatasetPath)
if err != nil {
t.Error(err)
return
}
defer d.Close()
gr1 := make(chan bool)
gr2 := make(chan bool)
go func() {
for i := 0; i < 30; i++ {
println("reload properties:", i)
// d.SetProperty(zfs.DatasetPropMountpoint, "/TEST")
d.ReloadProperties()
}
gr1 <- true
}()
go func() {
for i := 0; i < 100; i++ {
println("set mountpoint:", i)
d.SetProperty(zfs.DatasetPropMountpoint, "/TEST")
// d.GetProperty(zfs.DatasetPropMountpoint)
}
gr2 <- true
}()
d.SetProperty(zfs.DatasetPropMountpoint, "none")
<-gr1
<-gr2
}
/* ------------------------------------------------------------------------- */ /* ------------------------------------------------------------------------- */
// EXAMPLES: // EXAMPLES: