- Fix: Concurrent access to properties even for a different pools still cause crash

Prevent concurrent access to all properties with global mutex
This commit is contained in:
Faruk Kasumovic 2020-02-12 08:35:49 +01:00
parent 7c7ef79611
commit e24c488fee
2 changed files with 15 additions and 21 deletions

30
zfs.go
View File

@ -311,6 +311,8 @@ func (d *Dataset) PoolName() string {
// ReloadProperties re-read dataset's properties // ReloadProperties re-read dataset's properties
func (d *Dataset) ReloadProperties() (err error) { func (d *Dataset) ReloadProperties() (err error) {
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
if d.list == nil { if d.list == nil {
err = errors.New(msgDatasetIsNil) err = errors.New(msgDatasetIsNil)
return return
@ -318,15 +320,7 @@ func (d *Dataset) ReloadProperties() (err error) {
d.Properties = make(map[Prop]Property) d.Properties = make(map[Prop]Property)
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++ {
var plist *C.property_list_t plist := C.read_dataset_property(d.list, C.int(prop))
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
} }
@ -340,15 +334,12 @@ func (d *Dataset) ReloadProperties() (err error) {
// GetProperty reload and return single specified property. This also reloads requested // GetProperty reload and return single specified property. This also reloads requested
// property in Properties map. // property in Properties map.
func (d *Dataset) GetProperty(p Prop) (prop Property, err error) { func (d *Dataset) GetProperty(p Prop) (prop Property, err error) {
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
if d.list == nil { if d.list == nil {
err = errors.New(msgDatasetIsNil) err = errors.New(msgDatasetIsNil)
return return
} }
if p == DatasetPropMounted || p == DatasetPropMountpoint {
// 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()
@ -363,6 +354,8 @@ func (d *Dataset) GetProperty(p Prop) (prop Property, err error) {
// GetUserProperty - lookup and return user propery // GetUserProperty - lookup and return user propery
func (d *Dataset) GetUserProperty(p string) (prop Property, err error) { func (d *Dataset) GetUserProperty(p string) (prop Property, err error) {
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
if d.list == nil { if d.list == nil {
err = errors.New(msgDatasetIsNil) err = errors.New(msgDatasetIsNil)
return return
@ -384,16 +377,13 @@ func (d *Dataset) GetUserProperty(p string) (prop Property, err error) {
// some can be set only at creation time and some are read only. // some can be set only at creation time and some are read only.
// Always check if returned error and its description. // Always check if returned error and its description.
func (d *Dataset) SetProperty(p Prop, value string) (err error) { func (d *Dataset) SetProperty(p Prop, value string) (err error) {
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
if d.list == nil { if d.list == nil {
err = errors.New(msgDatasetIsNil) err = errors.New(msgDatasetIsNil)
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 {
@ -414,6 +404,8 @@ func (d *Dataset) SetProperty(p Prop, value string) (err error) {
// SetUserProperty - // SetUserProperty -
func (d *Dataset) SetUserProperty(prop, value string) (err error) { func (d *Dataset) SetUserProperty(prop, value string) (err error) {
Global.Mtx.Lock()
defer Global.Mtx.Unlock()
if d.list == nil { if d.list == nil {
err = errors.New(msgDatasetIsNil) err = errors.New(msgDatasetIsNil)
return return

View File

@ -214,7 +214,7 @@ func zfsTestMountPointConcurrency(t *testing.T) {
gr1 := make(chan bool) gr1 := make(chan bool)
gr2 := make(chan bool) gr2 := make(chan bool)
go func() { go func() {
for i := 0; i < 30; i++ { for i := 0; i < 100; i++ {
println("reload properties:", i) println("reload properties:", i)
// d.SetProperty(zfs.DatasetPropMountpoint, "/TEST") // d.SetProperty(zfs.DatasetPropMountpoint, "/TEST")
d.ReloadProperties() d.ReloadProperties()
@ -224,11 +224,13 @@ func zfsTestMountPointConcurrency(t *testing.T) {
go func() { go func() {
for i := 0; i < 100; i++ { for i := 0; i < 100; i++ {
println("set mountpoint:", i) println("set mountpoint:", i)
d.SetProperty(zfs.DatasetPropMountpoint, "/TEST") d.ReloadProperties()
// d.SetProperty(zfs.DatasetPropMountpoint, "/TEST")
// d.GetProperty(zfs.DatasetPropMountpoint) // d.GetProperty(zfs.DatasetPropMountpoint)
} }
gr2 <- true gr2 <- true
}() }()
d.SetProperty(zfs.DatasetPropMountpoint, "none") d.SetProperty(zfs.DatasetPropMountpoint, "none")
<-gr1 <-gr1