Compare commits
2 Commits
8cd48ad68b
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 9415866801 | |||
| 9ce17287a5 |
@@ -28,7 +28,7 @@ func rpm_load_rpm(full_path string, checksum_type RpmChecksumType, location_href
|
||||
var stat_mod_time int64
|
||||
var stat_size int64
|
||||
|
||||
pkg, err = rpm_package_from_rpm_base(full_path, change_log_limit)
|
||||
pkg, err = RpmPackageFromRpmBase(full_path, change_log_limit)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("can not get rpm header: %v", err)
|
||||
}
|
||||
@@ -47,6 +47,10 @@ func rpm_load_rpm(full_path string, checksum_type RpmChecksumType, location_href
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while checksum calculation: %v", err)
|
||||
}
|
||||
|
||||
// since we did calcualte the checksum with our own type,
|
||||
// we need to override the chekcsum type name
|
||||
pkg.RpmChecksumType = RpmChecksumName(checksum_type)
|
||||
return pkg, nil
|
||||
}
|
||||
|
||||
@@ -65,7 +69,7 @@ func rpm_get_checksum(file_path string, checksum_type RpmChecksumType, cache_dir
|
||||
return checksum, nil
|
||||
}
|
||||
|
||||
func rpm_package_from_rpm_base(full_path string, change_log_limit int) (*RpmPackage, error) {
|
||||
func RpmPackageFromRpmBase(full_path string, change_log_limit int) (*RpmPackage, error) {
|
||||
var f *os.File
|
||||
var err error
|
||||
var rpm *rpmutils.Rpm
|
||||
@@ -207,7 +211,8 @@ func rpm_package_from_rpm_base(full_path string, change_log_limit int) (*RpmPack
|
||||
|
||||
fda, err = rpm.Header.GetUint64(rpmutils.FILEDIGESTALGO)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
// default to md5. i assume the rpm file uses md5 when this field is missing
|
||||
fda = rpmutils.PGPHASHALGO_MD5
|
||||
}
|
||||
pkg.RpmChecksumType = rpmutils.GetFileAlgoName(int(fda))
|
||||
|
||||
|
||||
91
rpm-repo.go
91
rpm-repo.go
@@ -1,6 +1,7 @@
|
||||
package repokit
|
||||
|
||||
import "encoding/xml"
|
||||
import "errors"
|
||||
import "fmt"
|
||||
import "io"
|
||||
import "io/fs"
|
||||
@@ -11,6 +12,7 @@ import "sort"
|
||||
import "strconv"
|
||||
import "strings"
|
||||
import "sync"
|
||||
import "syscall"
|
||||
import "time"
|
||||
|
||||
type RpmLockMode int
|
||||
@@ -97,13 +99,12 @@ func RpmCreateRepoWithOptions(dir string, options RpmRepoOptions) error {
|
||||
}
|
||||
|
||||
|
||||
func rpm_create_rpm_repo(dir string, options RpmRepoOptions) error {
|
||||
func rpm_create_rpm_repo(dir string, options RpmRepoOptions) (ret_err error) {
|
||||
var in_dir string
|
||||
var in_repo string
|
||||
var out_dir string
|
||||
var out_repo string
|
||||
var tmp_out_dir string
|
||||
var lock_dir string
|
||||
var out_dir_exist bool
|
||||
var err error
|
||||
var pool_tasks []*rpm_pool_task
|
||||
@@ -135,13 +136,13 @@ func rpm_create_rpm_repo(dir string, options RpmRepoOptions) error {
|
||||
var data rpm_user_data
|
||||
var compression_type RpmCompressionType
|
||||
var lock_mode RpmLockMode
|
||||
var lock_handle *os.File
|
||||
|
||||
in_dir = rpm_normalize_dir_path(dir)
|
||||
in_repo = path.Join(in_dir, "repodata")
|
||||
out_dir = options.OutputDir
|
||||
out_repo = ""
|
||||
tmp_out_dir = ""
|
||||
lock_dir = ""
|
||||
|
||||
if out_dir != "" {
|
||||
out_dir_exist, err = rpm_path_exists(out_dir)
|
||||
@@ -173,11 +174,21 @@ lock_mode = options.LockMode
|
||||
}
|
||||
}
|
||||
|
||||
lock_dir, tmp_out_dir, err = rpm_lock_repo(out_dir, lock_mode)
|
||||
lock_handle, tmp_out_dir, err = rpm_lock_repo(out_dir, lock_mode)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error to lock repo: %v", err)
|
||||
}
|
||||
defer rpm_exit_cleanup(lock_dir)
|
||||
defer func() {
|
||||
var unlock_err error
|
||||
|
||||
rpm_exit_cleanup(tmp_out_dir)
|
||||
unlock_err = rpm_unlock_repo(lock_handle)
|
||||
if ret_err == nil && unlock_err != nil {
|
||||
// i can't use the return statement because it is
|
||||
// set in a deferred function.
|
||||
ret_err = fmt.Errorf("unlock repo lock failed: %w", unlock_err)
|
||||
}
|
||||
}()
|
||||
|
||||
jobs = make(chan *rpm_repo_job, 100)
|
||||
results = make(chan error, 100)
|
||||
@@ -504,45 +515,61 @@ func rpm_exit_cleanup(lock_dir string) {
|
||||
_ = os.RemoveAll(lock_dir)
|
||||
}
|
||||
|
||||
func rpm_lock_repo(repo_dir string, lock_mode RpmLockMode) (string, string, error) {
|
||||
var lock_dir string
|
||||
func rpm_lock_repo(repo_dir string, lock_mode RpmLockMode) (*os.File, string, error) {
|
||||
var lock_handle *os.File
|
||||
var tmp_repodata_dir string
|
||||
var err error
|
||||
var sleep_duration time.Duration
|
||||
var flock_flags int
|
||||
|
||||
lock_dir = filepath.Join(repo_dir, ".repodata")
|
||||
if lock_mode == RpmLockNone {
|
||||
tmp_repodata_dir = filepath.Join(repo_dir, rpm_append_pid_and_datetime(".repodata", ""))
|
||||
err = os.Mkdir(tmp_repodata_dir, 0775)
|
||||
if lock_mode != RpmLockNone {
|
||||
lock_handle, err = os.Open(repo_dir)
|
||||
if err != nil {
|
||||
return "", "", fmt.Errorf("cannot create %s: %w", tmp_repodata_dir, err)
|
||||
}
|
||||
return "", tmp_repodata_dir, nil
|
||||
}
|
||||
|
||||
sleep_duration = 500 * time.Millisecond
|
||||
for {
|
||||
err = os.Mkdir(lock_dir, 0775)
|
||||
if err == nil {
|
||||
tmp_repodata_dir = lock_dir
|
||||
return lock_dir, tmp_repodata_dir, nil
|
||||
}
|
||||
|
||||
if !os.IsExist(err) {
|
||||
return "", "", fmt.Errorf("error while creating temporary repodata directory: %s: %w", lock_dir, err)
|
||||
return nil, "", fmt.Errorf("cannot open repo dir %s for locking: %w", repo_dir, err)
|
||||
}
|
||||
|
||||
flock_flags = syscall.LOCK_EX
|
||||
if lock_mode == RpmLockFail {
|
||||
return "", "", fmt.Errorf("repodata lock exists: %s", lock_dir)
|
||||
flock_flags = flock_flags | syscall.LOCK_NB
|
||||
} else if lock_mode != RpmLockWait {
|
||||
lock_handle.Close()
|
||||
return nil, "", fmt.Errorf("unknown lock mode")
|
||||
}
|
||||
|
||||
if lock_mode == RpmLockWait {
|
||||
time.Sleep(sleep_duration)
|
||||
continue
|
||||
err = syscall.Flock(int(lock_handle.Fd()), flock_flags)
|
||||
if err != nil {
|
||||
lock_handle.Close()
|
||||
if errors.Is(err, syscall.EWOULDBLOCK) || errors.Is(err, syscall.EAGAIN) {
|
||||
return nil, "", fmt.Errorf("repo dir lock exists: %s", repo_dir)
|
||||
}
|
||||
return nil, "", fmt.Errorf("cannot lock repo dir %s: %w", repo_dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
return "", "", fmt.Errorf("unknown lock mode")
|
||||
tmp_repodata_dir = filepath.Join(repo_dir, rpm_append_pid_and_datetime(".repodata", ""))
|
||||
err = os.Mkdir(tmp_repodata_dir, os.ModePerm)
|
||||
if err != nil {
|
||||
rpm_unlock_repo(lock_handle)
|
||||
return nil, "", fmt.Errorf("cannot create %s: %w", tmp_repodata_dir, err)
|
||||
}
|
||||
|
||||
return lock_handle, tmp_repodata_dir, nil
|
||||
}
|
||||
|
||||
func rpm_unlock_repo(lock_handle *os.File) error {
|
||||
var err error
|
||||
|
||||
if lock_handle == nil {
|
||||
// in case rpm_lock_repo() didn't really lock a repo or did fail.
|
||||
return nil
|
||||
}
|
||||
|
||||
err = syscall.Flock(int(lock_handle.Fd()), syscall.LOCK_UN)
|
||||
if err != nil {
|
||||
lock_handle.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
return lock_handle.Close()
|
||||
}
|
||||
|
||||
func rpm_append_pid_and_datetime(prefix string, suffix string) string {
|
||||
|
||||
70
rpm_test.go
70
rpm_test.go
@@ -164,21 +164,21 @@ func TestRpmNormalizeDirPathWindowsDrive(t *testing.T) {
|
||||
|
||||
func TestRpmLockRepoModes(t *testing.T) {
|
||||
var dir string
|
||||
var lock_dir string
|
||||
var lock_handle *os.File
|
||||
var tmp_dir string
|
||||
var err error
|
||||
var wait_done chan error
|
||||
var remove_err error
|
||||
var exists bool
|
||||
var wait_err error
|
||||
var lock_err error
|
||||
|
||||
dir = t.TempDir()
|
||||
|
||||
lock_dir = filepath.Join(dir, ".repodata")
|
||||
err = os.Mkdir(lock_dir, 0775)
|
||||
lock_handle, tmp_dir, err = rpm_lock_repo(dir, RpmLockWait)
|
||||
if err != nil {
|
||||
t.Fatalf("mkdir lock dir: %v", err)
|
||||
t.Fatalf("initial lock: %v", err)
|
||||
}
|
||||
defer rpm_exit_cleanup(tmp_dir)
|
||||
|
||||
_, _, err = rpm_lock_repo(dir, RpmLockFail)
|
||||
if err == nil {
|
||||
@@ -187,23 +187,25 @@ func TestRpmLockRepoModes(t *testing.T) {
|
||||
|
||||
wait_done = make(chan error, 1)
|
||||
go func() {
|
||||
var tmp_lock string
|
||||
var tmp_lock *os.File
|
||||
var tmp_out string
|
||||
var inner_err error
|
||||
|
||||
tmp_lock, tmp_out, inner_err = rpm_lock_repo(dir, RpmLockWait)
|
||||
if inner_err == nil {
|
||||
if tmp_lock == "" || tmp_out == "" {
|
||||
if tmp_lock == nil || tmp_out == "" {
|
||||
inner_err = fmt.Errorf("RpmLockWait returned empty paths")
|
||||
}
|
||||
_ = rpm_unlock_repo(tmp_lock)
|
||||
rpm_exit_cleanup(tmp_out)
|
||||
}
|
||||
wait_done <- inner_err
|
||||
}()
|
||||
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
remove_err = os.RemoveAll(lock_dir)
|
||||
if remove_err != nil {
|
||||
t.Fatalf("remove lock dir: %v", remove_err)
|
||||
lock_err = rpm_unlock_repo(lock_handle)
|
||||
if lock_err != nil {
|
||||
t.Fatalf("unlock lock handle: %v", lock_err)
|
||||
}
|
||||
|
||||
wait_err = <-wait_done
|
||||
@@ -211,12 +213,12 @@ func TestRpmLockRepoModes(t *testing.T) {
|
||||
t.Fatalf("RpmLockWait error: %v", wait_err)
|
||||
}
|
||||
|
||||
lock_dir, tmp_dir, err = rpm_lock_repo(dir, RpmLockNone)
|
||||
lock_handle, tmp_dir, err = rpm_lock_repo(dir, RpmLockNone)
|
||||
if err != nil {
|
||||
t.Fatalf("RpmLockNone error: %v", err)
|
||||
}
|
||||
if lock_dir != "" {
|
||||
t.Fatalf("RpmLockNone should not return lock dir, got %q", lock_dir)
|
||||
if lock_handle != nil {
|
||||
t.Fatalf("RpmLockNone should not return lock handle")
|
||||
}
|
||||
exists, err = rpm_path_exists(tmp_dir)
|
||||
if err != nil {
|
||||
@@ -225,4 +227,46 @@ func TestRpmLockRepoModes(t *testing.T) {
|
||||
if !exists {
|
||||
t.Fatalf("RpmLockNone tmp dir missing: %q", tmp_dir)
|
||||
}
|
||||
rpm_exit_cleanup(tmp_dir)
|
||||
}
|
||||
|
||||
func TestRpmCreateRepoWithOptionsLockNone(t *testing.T) {
|
||||
var dir string
|
||||
var options RpmRepoOptions
|
||||
var err error
|
||||
var repodata_dir string
|
||||
var repomd_path string
|
||||
var exists bool
|
||||
var entries []os.DirEntry
|
||||
var entry os.DirEntry
|
||||
|
||||
dir = t.TempDir()
|
||||
options = RpmDefaultRepoOptions()
|
||||
options.LockMode = RpmLockNone
|
||||
options.AllowMissingRepomd = true
|
||||
|
||||
err = RpmCreateRepoWithOptions(dir, options)
|
||||
if err != nil {
|
||||
t.Fatalf("RpmCreateRepoWithOptions lock none: %v", err)
|
||||
}
|
||||
|
||||
repodata_dir = filepath.Join(dir, "repodata")
|
||||
repomd_path = filepath.Join(repodata_dir, "repomd.xml")
|
||||
exists, err = rpm_path_exists(repomd_path)
|
||||
if err != nil {
|
||||
t.Fatalf("repomd exists check: %v", err)
|
||||
}
|
||||
if !exists {
|
||||
t.Fatalf("repomd not found at %q", repomd_path)
|
||||
}
|
||||
|
||||
entries, err = os.ReadDir(dir)
|
||||
if err != nil {
|
||||
t.Fatalf("readdir repo dir: %v", err)
|
||||
}
|
||||
for _, entry = range entries {
|
||||
if strings.HasPrefix(entry.Name(), ".repodata.") {
|
||||
t.Fatalf("temporary repodata dir leaked: %q", entry.Name())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user