diff --git a/dfget/core/uploader/peer_server_executor.go b/dfget/core/uploader/peer_server_executor.go index 7979aee7a..8b3bdbcd2 100644 --- a/dfget/core/uploader/peer_server_executor.go +++ b/dfget/core/uploader/peer_server_executor.go @@ -21,12 +21,14 @@ import ( "io" "os" "os/exec" + "path/filepath" "strconv" "strings" "sync/atomic" "time" "github.com/dragonflyoss/Dragonfly/dfget/config" + "github.com/dragonflyoss/Dragonfly/pkg/fileutils" "github.com/dragonflyoss/Dragonfly/version" "github.com/sirupsen/logrus" @@ -74,6 +76,12 @@ func (pe *peerServerExecutor) StartPeerServerProcess(cfg *config.Config) (port i return port, nil } + fileLock := fileutils.NewFileLock(filepath.Dir(cfg.RV.MetaPath)) + if err = fileLock.Lock(); err != nil { + return 0, err + } + defer fileLock.Unlock() + cmd := exec.Command(os.Args[0], "server", "--ip", cfg.RV.LocalIP, "--port", strconv.Itoa(cfg.RV.PeerPort), diff --git a/pkg/fileutils/filelock.go b/pkg/fileutils/filelock.go new file mode 100644 index 000000000..27b580502 --- /dev/null +++ b/pkg/fileutils/filelock.go @@ -0,0 +1,78 @@ +/* + * Copyright The Dragonfly Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fileutils + +import ( + "fmt" + "os" + "syscall" + + "github.com/pkg/errors" +) + +// FileLock defines a file lock implemented by syscall.Flock +type FileLock struct { + fileName string + fd *os.File +} + +// NewFileLock create a FileLock instance +func NewFileLock(name string) *FileLock { + return &FileLock{ + fileName: name, + } +} + +// Lock locks file. +// If the file is already locked, the calling goroutine blocks until the file is unlocked. +// If lock has been invoked without unlock, lock again will return an error. +func (l *FileLock) Lock() error { + var ( + fd *os.File + err error + ) + + if l.fd != nil { + return fmt.Errorf("file %s has already been locked", l.fileName) + } + + if fd, err = os.Open(l.fileName); err != nil { + return err + } + l.fd = fd + + if err := syscall.Flock(int(l.fd.Fd()), syscall.LOCK_EX); err != nil { + return errors.Wrapf(err, "file %s lock failed", l.fileName) + } + return nil +} + +// Unlock unlocks file. +// If lock has not been invoked before unlock, unlock will return an error. +func (l *FileLock) Unlock() error { + if l.fd == nil { + return fmt.Errorf("file %s descriptor is nil", l.fileName) + } + fd := l.fd + l.fd = nil + + defer fd.Close() + if err := syscall.Flock(int(fd.Fd()), syscall.LOCK_UN); err != nil { + return errors.Wrapf(err, "file %s unlock failed", l.fileName) + } + return nil +} diff --git a/pkg/fileutils/filelock_test.go b/pkg/fileutils/filelock_test.go new file mode 100644 index 000000000..f2cc5f833 --- /dev/null +++ b/pkg/fileutils/filelock_test.go @@ -0,0 +1,76 @@ +/* + * Copyright The Dragonfly Authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package fileutils + +import ( + "fmt" + "io/ioutil" + "os" + "time" + + "github.com/go-check/check" +) + +type FileLockTestSuite struct { + tmpDir string + flockA *FileLock + flockB *FileLock +} + +func init() { + check.Suite(&FileLockTestSuite{}) +} + +func (s *FileLockTestSuite) SetUpSuite(c *check.C) { + tmpDir, _ := ioutil.TempDir("/tmp", "dfget-FileLockTestSuite-") + os.Create(tmpDir) + s.tmpDir = tmpDir + s.flockA = NewFileLock(tmpDir) + s.flockB = NewFileLock(tmpDir) +} + +func (s *FileLockTestSuite) TearDownSuite(c *check.C) { + if s.tmpDir != "" { + if err := os.RemoveAll(s.tmpDir); err != nil { + fmt.Printf("remove path:%s error", s.tmpDir) + } + } +} + +func (s *FileLockTestSuite) TestFileLock(c *check.C) { + err := s.flockA.Lock() + c.Assert(err, check.IsNil) + + err = s.flockA.Lock() + c.Assert(err, check.NotNil) + + err = s.flockA.Unlock() + c.Assert(err, check.IsNil) + + err = s.flockA.Unlock() + c.Assert(err, check.NotNil) + + s.flockA.Lock() + start := time.Now() + go func() { + time.Sleep(time.Second) + s.flockA.Unlock() + }() + s.flockB.Lock() + c.Assert(time.Since(start) >= time.Second, check.Equals, true) + s.flockB.Unlock() +}