Jelajahi Sumber

projectquota: protect concurrent map access

Protect access to q.quotas map, and lock around changing nextProjectID.

Techinically, the lock in findNextProjectID() is not needed as it is
only called during initialization, but one can never be too careful.

Fixes: 52897d1c092 ("projectquota: utility class for project quota controls")
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
(cherry picked from commit 1ac0a66a64a906911d0708cd0e5fa397a2f0b595)
Signed-off-by: Kir Kolyshkin <kolyshkin@gmail.com>
Kir Kolyshkin 6 tahun lalu
induk
melakukan
b0f01be33f

+ 9 - 3
daemon/graphdriver/quota/projectquota.go

@@ -153,9 +153,11 @@ func NewControl(basePath string) (*Control, error) {
 // SetQuota - assign a unique project id to directory and set the quota limits
 // SetQuota - assign a unique project id to directory and set the quota limits
 // for that project id
 // for that project id
 func (q *Control) SetQuota(targetPath string, quota Quota) error {
 func (q *Control) SetQuota(targetPath string, quota Quota) error {
-
+	q.RLock()
 	projectID, ok := q.quotas[targetPath]
 	projectID, ok := q.quotas[targetPath]
+	q.RUnlock()
 	if !ok {
 	if !ok {
+		q.Lock()
 		projectID = q.nextProjectID
 		projectID = q.nextProjectID
 
 
 		//
 		//
@@ -163,11 +165,12 @@ func (q *Control) SetQuota(targetPath string, quota Quota) error {
 		//
 		//
 		err := setProjectID(targetPath, projectID)
 		err := setProjectID(targetPath, projectID)
 		if err != nil {
 		if err != nil {
+			q.Unlock()
 			return err
 			return err
 		}
 		}
-
 		q.quotas[targetPath] = projectID
 		q.quotas[targetPath] = projectID
 		q.nextProjectID++
 		q.nextProjectID++
+		q.Unlock()
 	}
 	}
 
 
 	//
 	//
@@ -204,8 +207,9 @@ func setProjectQuota(backingFsBlockDev string, projectID uint32, quota Quota) er
 
 
 // GetQuota - get the quota limits of a directory that was configured with SetQuota
 // GetQuota - get the quota limits of a directory that was configured with SetQuota
 func (q *Control) GetQuota(targetPath string, quota *Quota) error {
 func (q *Control) GetQuota(targetPath string, quota *Quota) error {
-
+	q.RLock()
 	projectID, ok := q.quotas[targetPath]
 	projectID, ok := q.quotas[targetPath]
+	q.RUnlock()
 	if !ok {
 	if !ok {
 		return errors.Errorf("quota not found for path: %s", targetPath)
 		return errors.Errorf("quota not found for path: %s", targetPath)
 	}
 	}
@@ -276,6 +280,8 @@ func setProjectID(targetPath string, projectID uint32) error {
 // findNextProjectID - find the next project id to be used for containers
 // findNextProjectID - find the next project id to be used for containers
 // by scanning driver home directory to find used project ids
 // by scanning driver home directory to find used project ids
 func (q *Control) findNextProjectID(home string) error {
 func (q *Control) findNextProjectID(home string) error {
+	q.Lock()
+	defer q.Unlock()
 	files, err := ioutil.ReadDir(home)
 	files, err := ioutil.ReadDir(home)
 	if err != nil {
 	if err != nil {
 		return errors.Errorf("read directory failed: %s", home)
 		return errors.Errorf("read directory failed: %s", home)

+ 3 - 0
daemon/graphdriver/quota/types.go

@@ -2,6 +2,8 @@
 
 
 package quota // import "github.com/docker/docker/daemon/graphdriver/quota"
 package quota // import "github.com/docker/docker/daemon/graphdriver/quota"
 
 
+import "sync"
+
 // Quota limit params - currently we only control blocks hard limit
 // Quota limit params - currently we only control blocks hard limit
 type Quota struct {
 type Quota struct {
 	Size uint64
 	Size uint64
@@ -11,6 +13,7 @@ type Quota struct {
 // who wants to apply project quotas to container dirs
 // who wants to apply project quotas to container dirs
 type Control struct {
 type Control struct {
 	backingFsBlockDev string
 	backingFsBlockDev string
+	sync.RWMutex      // protect nextProjectID and quotas map
 	nextProjectID     uint32
 	nextProjectID     uint32
 	quotas            map[string]uint32
 	quotas            map[string]uint32
 }
 }