Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit c0fd10c

Browse files
authored
Merge pull request #700 from jfontan/improvement/support-non-rw-filesystems
Add a setRef and rewritePackedRefsWhileLocked versions that supports non rw fs
2 parents 8ef34f6 + b869eb1 commit c0fd10c

6 files changed

+142
-33
lines changed

storage/filesystem/internal/dotgit/dotgit.go

+6-30
Original file line numberDiff line numberDiff line change
@@ -323,36 +323,9 @@ func (d *DotGit) SetRef(r, old *plumbing.Reference) error {
323323
content = fmt.Sprintln(r.Hash().String())
324324
}
325325

326-
// If we are not checking an old ref, just truncate the file.
327-
mode := os.O_RDWR | os.O_CREATE
328-
if old == nil {
329-
mode |= os.O_TRUNC
330-
}
331-
332-
f, err := d.fs.OpenFile(r.Name().String(), mode, 0666)
333-
if err != nil {
334-
return err
335-
}
336-
337-
defer ioutil.CheckClose(f, &err)
338-
339-
// Lock is unlocked by the deferred Close above. This is because Unlock
340-
// does not imply a fsync and thus there would be a race between
341-
// Unlock+Close and other concurrent writers. Adding Sync to go-billy
342-
// could work, but this is better (and avoids superfluous syncs).
343-
err = f.Lock()
344-
if err != nil {
345-
return err
346-
}
347-
348-
// this is a no-op to call even when old is nil.
349-
err = d.checkReferenceAndTruncate(f, old)
350-
if err != nil {
351-
return err
352-
}
326+
fileName := r.Name().String()
353327

354-
_, err = f.Write([]byte(content))
355-
return err
328+
return d.setRef(fileName, content, old)
356329
}
357330

358331
// Refs scans the git directory collecting references, which it returns.
@@ -485,7 +458,10 @@ func (d *DotGit) openAndLockPackedRefs(doCreate bool) (
485458
}
486459
}()
487460

488-
openFlags := os.O_RDWR
461+
// File mode is retrieved from a constant defined in the target specific
462+
// files (dotgit_rewrite_packed_refs_*). Some modes are not available
463+
// in all filesystems.
464+
openFlags := openAndLockPackedRefsMode
489465
if doCreate {
490466
openFlags |= os.O_CREATE
491467
}

storage/filesystem/internal/dotgit/dotgit_rewrite_packed_refs_nix.go

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
1-
// +build !windows
1+
// +build !windows,!norwfs
22

33
package dotgit
44

5-
import "gopkg.in/src-d/go-billy.v4"
5+
import (
6+
"os"
7+
8+
"gopkg.in/src-d/go-billy.v4"
9+
)
10+
11+
const openAndLockPackedRefsMode = os.O_RDWR
612

713
func (d *DotGit) rewritePackedRefsWhileLocked(
814
tmp billy.File, pr billy.File) error {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// +build norwfs
2+
3+
package dotgit
4+
5+
import (
6+
"io"
7+
"os"
8+
9+
"gopkg.in/src-d/go-billy.v4"
10+
)
11+
12+
const openAndLockPackedRefsMode = os.O_RDONLY
13+
14+
// Instead of renaming that can not be supported in simpler filesystems
15+
// a full copy is done.
16+
func (d *DotGit) rewritePackedRefsWhileLocked(
17+
tmp billy.File, pr billy.File) error {
18+
19+
prWrite, err := d.fs.Create(pr.Name())
20+
if err != nil {
21+
return err
22+
}
23+
24+
defer prWrite.Close()
25+
26+
_, err = tmp.Seek(0, io.SeekStart)
27+
if err != nil {
28+
return err
29+
}
30+
31+
_, err = io.Copy(prWrite, tmp)
32+
33+
return err
34+
}

storage/filesystem/internal/dotgit/dotgit_rewrite_packed_refs_windows.go

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1-
// +build windows
1+
// +build windows,!norwfs
22

33
package dotgit
44

55
import (
66
"io"
7+
"os"
78

89
"gopkg.in/src-d/go-billy.v4"
910
)
1011

12+
const openAndLockPackedRefsMode = os.O_RDWR
13+
1114
func (d *DotGit) rewritePackedRefsWhileLocked(
1215
tmp billy.File, pr billy.File) error {
1316
// If we aren't using the bare Windows filesystem as the storage
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// +build !norwfs
2+
3+
package dotgit
4+
5+
import (
6+
"os"
7+
8+
"gopkg.in/src-d/go-git.v4/plumbing"
9+
"gopkg.in/src-d/go-git.v4/utils/ioutil"
10+
)
11+
12+
func (d *DotGit) setRef(fileName, content string, old *plumbing.Reference) error {
13+
// If we are not checking an old ref, just truncate the file.
14+
mode := os.O_RDWR | os.O_CREATE
15+
if old == nil {
16+
mode |= os.O_TRUNC
17+
}
18+
19+
f, err := d.fs.OpenFile(fileName, mode, 0666)
20+
if err != nil {
21+
return err
22+
}
23+
24+
defer ioutil.CheckClose(f, &err)
25+
26+
// Lock is unlocked by the deferred Close above. This is because Unlock
27+
// does not imply a fsync and thus there would be a race between
28+
// Unlock+Close and other concurrent writers. Adding Sync to go-billy
29+
// could work, but this is better (and avoids superfluous syncs).
30+
err = f.Lock()
31+
if err != nil {
32+
return err
33+
}
34+
35+
// this is a no-op to call even when old is nil.
36+
err = d.checkReferenceAndTruncate(f, old)
37+
if err != nil {
38+
return err
39+
}
40+
41+
_, err = f.Write([]byte(content))
42+
return err
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// +build norwfs
2+
3+
package dotgit
4+
5+
import (
6+
"fmt"
7+
8+
"gopkg.in/src-d/go-git.v4/plumbing"
9+
)
10+
11+
// There are some filesystems that don't support opening files in RDWD mode.
12+
// In these filesystems the standard SetRef function can not be used as i
13+
// reads the reference file to check that it's not modified before updating it.
14+
//
15+
// This version of the function writes the reference without extra checks
16+
// making it compatible with these simple filesystems. This is usually not
17+
// a problem as they should be accessed by only one process at a time.
18+
func (d *DotGit) setRef(fileName, content string, old *plumbing.Reference) error {
19+
_, err := d.fs.Stat(fileName)
20+
if err == nil && old != nil {
21+
fRead, err := d.fs.Open(fileName)
22+
if err != nil {
23+
return err
24+
}
25+
26+
ref, err := d.readReferenceFrom(fRead, old.Name().String())
27+
fRead.Close()
28+
29+
if err != nil {
30+
return err
31+
}
32+
33+
if ref.Hash() != old.Hash() {
34+
return fmt.Errorf("reference has changed concurrently")
35+
}
36+
}
37+
38+
f, err := d.fs.Create(fileName)
39+
if err != nil {
40+
return err
41+
}
42+
43+
defer f.Close()
44+
45+
_, err = f.Write([]byte(content))
46+
return err
47+
}

0 commit comments

Comments
 (0)