Function to create symlinks atomically
This commit is contained in:
parent
fa45324d39
commit
fbaaca1be9
@ -12,6 +12,30 @@ import (
|
|||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
|
|
||||||
|
func AtomicCreateSymlink(oldname, newname string) (err error) {
|
||||||
|
err = os.Symlink(oldname, newname)
|
||||||
|
if err == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if !errors.Is(err, fs.ErrExist) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for {
|
||||||
|
tempname := newname + RandomFilename()
|
||||||
|
err = os.Symlink(oldname, tempname)
|
||||||
|
if err == nil {
|
||||||
|
err = os.Rename(tempname, newname)
|
||||||
|
if err != nil {
|
||||||
|
os.Remove(tempname)
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !errors.Is(err, fs.ErrExist) {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func AtomicWriteFile(path string, data []byte, perm os.FileMode) (err error) {
|
func AtomicWriteFile(path string, data []byte, perm os.FileMode) (err error) {
|
||||||
npath, err := filepath.EvalSymlinks(path)
|
npath, err := filepath.EvalSymlinks(path)
|
||||||
if errors.Is(err, fs.ErrNotExist) {
|
if errors.Is(err, fs.ErrNotExist) {
|
||||||
|
|||||||
@ -3,11 +3,17 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base32"
|
||||||
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
|
not_rand "math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"os/exec"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
@ -57,9 +63,9 @@ func Abspath(path string) string {
|
|||||||
return path
|
return path
|
||||||
}
|
}
|
||||||
|
|
||||||
var config_dir, kitty_exe, cache_dir string
|
var config_dir, kitty_exe, cache_dir, runtime_dir string
|
||||||
var kitty_exe_err error
|
var kitty_exe_err error
|
||||||
var config_dir_once, kitty_exe_once, cache_dir_once sync.Once
|
var config_dir_once, kitty_exe_once, cache_dir_once, runtime_dir_once sync.Once
|
||||||
|
|
||||||
func find_kitty_exe() {
|
func find_kitty_exe() {
|
||||||
exe, err := os.Executable()
|
exe, err := os.Executable()
|
||||||
@ -133,6 +139,60 @@ func CacheDir() string {
|
|||||||
return cache_dir
|
return cache_dir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func macos_user_cache_dir() string {
|
||||||
|
// Sadly Go does not provide confstr() so we use this hack. We could
|
||||||
|
// Note that given a user generateduid and uid we can derive this by using
|
||||||
|
// the algorithm at https://github.com/ydkhatri/MacForensics/blob/master/darwin_path_generator.py
|
||||||
|
// but I cant find a good way to get the generateduid. Requires calling dscl in which case we might as well call getconf
|
||||||
|
// The data is in /var/db/dslocal/nodes/Default/users/<username>.plist but it needs root
|
||||||
|
matches, err := filepath.Glob("/private/var/folders/*/*/C")
|
||||||
|
if err == nil {
|
||||||
|
for _, m := range matches {
|
||||||
|
s, err := os.Stat(m)
|
||||||
|
if err == nil {
|
||||||
|
if stat, ok := s.Sys().(unix.Stat_t); ok && s.IsDir() && int(stat.Uid) == os.Geteuid() && s.Mode().Perm() == 0o700 && unix.Access(m, unix.X_OK|unix.W_OK|unix.R_OK) == nil {
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out, err := exec.Command("/usr/bin/getconf", "DARWIN_USER_CACHE_DIR").Output()
|
||||||
|
if err == nil {
|
||||||
|
return strings.TrimSpace(UnsafeBytesToString(out))
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func find_runtime_dir() {
|
||||||
|
var candidate string
|
||||||
|
if q := os.Getenv("KITTY_RUNTIME_DIRECTORY"); q != "" {
|
||||||
|
candidate = q
|
||||||
|
} else if runtime.GOOS == "darwin" {
|
||||||
|
candidate = macos_user_cache_dir()
|
||||||
|
} else if q := os.Getenv("XDG_RUNTIME_DIR"); q != "" {
|
||||||
|
candidate = q
|
||||||
|
}
|
||||||
|
candidate = strings.TrimRight(candidate, "/")
|
||||||
|
if candidate == "" {
|
||||||
|
q := fmt.Sprintf("/run/user/%d", os.Geteuid())
|
||||||
|
if s, err := os.Stat(q); err == nil && s.IsDir() && unix.Access(q, unix.X_OK|unix.R_OK|unix.W_OK) == nil {
|
||||||
|
candidate = q
|
||||||
|
} else {
|
||||||
|
candidate = filepath.Join(CacheDir(), "run")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.MkdirAll(candidate, 0o700)
|
||||||
|
if s, err := os.Stat(candidate); err == nil && s.Mode().Perm() != 0o700 {
|
||||||
|
os.Chmod(candidate, 0o700)
|
||||||
|
}
|
||||||
|
runtime_dir = candidate
|
||||||
|
}
|
||||||
|
|
||||||
|
func RuntimeDir() string {
|
||||||
|
runtime_dir_once.Do(find_runtime_dir)
|
||||||
|
return runtime_dir
|
||||||
|
}
|
||||||
|
|
||||||
type Walk_callback func(path, abspath string, d fs.DirEntry, err error) error
|
type Walk_callback func(path, abspath string, d fs.DirEntry, err error) error
|
||||||
|
|
||||||
func transform_symlink(path string) string {
|
func transform_symlink(path string) string {
|
||||||
@ -205,3 +265,13 @@ func WalkWithSymlink(dirpath string, callback Walk_callback, transformers ...fun
|
|||||||
seen: make(map[string]bool), real_callback: callback, transform_func: transform, needs_recurse_func: needs_symlink_recurse}
|
seen: make(map[string]bool), real_callback: callback, transform_func: transform, needs_recurse_func: needs_symlink_recurse}
|
||||||
return sw.walk(dirpath)
|
return sw.walk(dirpath)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func RandomFilename() string {
|
||||||
|
b := []byte{0, 0, 0, 0, 0, 0, 0, 0}
|
||||||
|
_, err := rand.Read(b)
|
||||||
|
if err != nil {
|
||||||
|
return strconv.FormatUint(uint64(not_rand.Uint32()), 16)
|
||||||
|
}
|
||||||
|
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@ -3,16 +3,12 @@
|
|||||||
package shm
|
package shm
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base32"
|
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
not_rand "math/rand"
|
|
||||||
"os"
|
"os"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"kitty/tools/cli"
|
"kitty/tools/cli"
|
||||||
@ -48,15 +44,6 @@ func prefix_and_suffix(pattern string) (prefix, suffix string, err error) {
|
|||||||
return prefix, suffix, nil
|
return prefix, suffix, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func next_random() string {
|
|
||||||
b := make([]byte, 8)
|
|
||||||
_, err := rand.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
return strconv.FormatUint(uint64(not_rand.Uint32()), 16)
|
|
||||||
}
|
|
||||||
return base32.StdEncoding.WithPadding(base32.NoPadding).EncodeToString(b)
|
|
||||||
}
|
|
||||||
|
|
||||||
type MMap interface {
|
type MMap interface {
|
||||||
Close() error
|
Close() error
|
||||||
Unlink() error
|
Unlink() error
|
||||||
|
|||||||
@ -8,10 +8,11 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"kitty/tools/utils"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
|
||||||
|
"kitty/tools/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
var _ = fmt.Print
|
var _ = fmt.Print
|
||||||
@ -96,7 +97,7 @@ func create_temp(pattern string, size uint64) (ans MMap, err error) {
|
|||||||
var f *os.File
|
var f *os.File
|
||||||
try := 0
|
try := 0
|
||||||
for {
|
for {
|
||||||
name := prefix + next_random() + suffix
|
name := prefix + utils.RandomFilename() + suffix
|
||||||
path := file_path_from_name(name)
|
path := file_path_from_name(name)
|
||||||
f, err = os.OpenFile(path, os.O_EXCL|os.O_CREATE|os.O_RDWR, 0600)
|
f, err = os.OpenFile(path, os.O_EXCL|os.O_CREATE|os.O_RDWR, 0600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@ -11,6 +11,8 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
|
"kitty/tools/utils"
|
||||||
|
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -127,7 +129,7 @@ func create_temp(pattern string, size uint64) (ans MMap, err error) {
|
|||||||
var f *os.File
|
var f *os.File
|
||||||
try := 0
|
try := 0
|
||||||
for {
|
for {
|
||||||
name := prefix + next_random() + suffix
|
name := prefix + utils.RandomFilename() + suffix
|
||||||
if len(name) > SHM_NAME_MAX {
|
if len(name) > SHM_NAME_MAX {
|
||||||
return nil, ErrPatternTooLong
|
return nil, ErrPatternTooLong
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user