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_mod_time int64
|
||||||
var stat_size 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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can not get rpm header: %v", err)
|
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 {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("error while checksum calculation: %v", err)
|
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
|
return pkg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,7 +69,7 @@ func rpm_get_checksum(file_path string, checksum_type RpmChecksumType, cache_dir
|
|||||||
return checksum, nil
|
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 f *os.File
|
||||||
var err error
|
var err error
|
||||||
var rpm *rpmutils.Rpm
|
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)
|
fda, err = rpm.Header.GetUint64(rpmutils.FILEDIGESTALGO)
|
||||||
if err != nil {
|
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))
|
pkg.RpmChecksumType = rpmutils.GetFileAlgoName(int(fda))
|
||||||
|
|
||||||
@@ -226,7 +231,7 @@ func rpm_package_from_rpm_base(full_path string, change_log_limit int) (*RpmPack
|
|||||||
pf = RpmPackageFile{}
|
pf = RpmPackageFile{}
|
||||||
pf.FullPath = file_info.Name()
|
pf.FullPath = file_info.Name()
|
||||||
pf.Digest = file_info.Digest()
|
pf.Digest = file_info.Digest()
|
||||||
if file_info.Mode()&^07777 == cpio.S_ISDIR {
|
if file_info.Mode() & ^07777 == cpio.S_ISDIR {
|
||||||
pf.Type = "dir"
|
pf.Type = "dir"
|
||||||
} else if file_info.Flags() & rpmutils.RPMFILE_GHOST != 0 {
|
} else if file_info.Flags() & rpmutils.RPMFILE_GHOST != 0 {
|
||||||
pf.Type = "ghost"
|
pf.Type = "ghost"
|
||||||
|
|||||||
97
rpm-repo.go
97
rpm-repo.go
@@ -1,6 +1,7 @@
|
|||||||
package repokit
|
package repokit
|
||||||
|
|
||||||
import "encoding/xml"
|
import "encoding/xml"
|
||||||
|
import "errors"
|
||||||
import "fmt"
|
import "fmt"
|
||||||
import "io"
|
import "io"
|
||||||
import "io/fs"
|
import "io/fs"
|
||||||
@@ -11,6 +12,7 @@ import "sort"
|
|||||||
import "strconv"
|
import "strconv"
|
||||||
import "strings"
|
import "strings"
|
||||||
import "sync"
|
import "sync"
|
||||||
|
import "syscall"
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
type RpmLockMode int
|
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_dir string
|
||||||
var in_repo string
|
var in_repo string
|
||||||
var out_dir string
|
var out_dir string
|
||||||
var out_repo string
|
var out_repo string
|
||||||
var tmp_out_dir string
|
var tmp_out_dir string
|
||||||
var lock_dir string
|
|
||||||
var out_dir_exist bool
|
var out_dir_exist bool
|
||||||
var err error
|
var err error
|
||||||
var pool_tasks []*rpm_pool_task
|
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 data rpm_user_data
|
||||||
var compression_type RpmCompressionType
|
var compression_type RpmCompressionType
|
||||||
var lock_mode RpmLockMode
|
var lock_mode RpmLockMode
|
||||||
|
var lock_handle *os.File
|
||||||
|
|
||||||
in_dir = rpm_normalize_dir_path(dir)
|
in_dir = rpm_normalize_dir_path(dir)
|
||||||
in_repo = path.Join(in_dir, "repodata")
|
in_repo = path.Join(in_dir, "repodata")
|
||||||
out_dir = options.OutputDir
|
out_dir = options.OutputDir
|
||||||
out_repo = ""
|
out_repo = ""
|
||||||
tmp_out_dir = ""
|
tmp_out_dir = ""
|
||||||
lock_dir = ""
|
|
||||||
|
|
||||||
if out_dir != "" {
|
if out_dir != "" {
|
||||||
out_dir_exist, err = rpm_path_exists(out_dir)
|
out_dir_exist, err = rpm_path_exists(out_dir)
|
||||||
@@ -164,7 +165,7 @@ func rpm_create_rpm_repo(dir string, options RpmRepoOptions) error {
|
|||||||
}
|
}
|
||||||
total_pkg_num = len(pool_tasks)
|
total_pkg_num = len(pool_tasks)
|
||||||
|
|
||||||
lock_mode = options.LockMode
|
lock_mode = options.LockMode
|
||||||
if lock_mode == RpmLockUnset {
|
if lock_mode == RpmLockUnset {
|
||||||
if options.IgnoreLock {
|
if options.IgnoreLock {
|
||||||
lock_mode = RpmLockNone
|
lock_mode = RpmLockNone
|
||||||
@@ -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 {
|
if err != nil {
|
||||||
return fmt.Errorf("error to lock repo: %v", err)
|
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)
|
jobs = make(chan *rpm_repo_job, 100)
|
||||||
results = make(chan error, 100)
|
results = make(chan error, 100)
|
||||||
@@ -297,7 +308,7 @@ lock_mode = options.LockMode
|
|||||||
}
|
}
|
||||||
|
|
||||||
if failed_pkg_num > 0 {
|
if failed_pkg_num > 0 {
|
||||||
return fmt.Errorf("finished with error: %d packages failed, %d packages succeeded", failed_pkg_num, total_pkg_num-failed_pkg_num)
|
return fmt.Errorf("finished with error: %d packages failed, %d packages succeeded", failed_pkg_num, total_pkg_num - failed_pkg_num)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -504,45 +515,61 @@ func rpm_exit_cleanup(lock_dir string) {
|
|||||||
_ = os.RemoveAll(lock_dir)
|
_ = os.RemoveAll(lock_dir)
|
||||||
}
|
}
|
||||||
|
|
||||||
func rpm_lock_repo(repo_dir string, lock_mode RpmLockMode) (string, string, error) {
|
func rpm_lock_repo(repo_dir string, lock_mode RpmLockMode) (*os.File, string, error) {
|
||||||
var lock_dir string
|
var lock_handle *os.File
|
||||||
var tmp_repodata_dir string
|
var tmp_repodata_dir string
|
||||||
var err error
|
var err error
|
||||||
var sleep_duration time.Duration
|
var flock_flags int
|
||||||
|
|
||||||
lock_dir = filepath.Join(repo_dir, ".repodata")
|
if lock_mode != RpmLockNone {
|
||||||
if lock_mode == RpmLockNone {
|
lock_handle, err = os.Open(repo_dir)
|
||||||
tmp_repodata_dir = filepath.Join(repo_dir, rpm_append_pid_and_datetime(".repodata", ""))
|
|
||||||
err = os.Mkdir(tmp_repodata_dir, 0775)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", fmt.Errorf("cannot create %s: %w", tmp_repodata_dir, err)
|
return nil, "", fmt.Errorf("cannot open repo dir %s for locking: %w", repo_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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
flock_flags = syscall.LOCK_EX
|
||||||
if lock_mode == RpmLockFail {
|
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 {
|
err = syscall.Flock(int(lock_handle.Fd()), flock_flags)
|
||||||
time.Sleep(sleep_duration)
|
if err != nil {
|
||||||
continue
|
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 {
|
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) {
|
func TestRpmLockRepoModes(t *testing.T) {
|
||||||
var dir string
|
var dir string
|
||||||
var lock_dir string
|
var lock_handle *os.File
|
||||||
var tmp_dir string
|
var tmp_dir string
|
||||||
var err error
|
var err error
|
||||||
var wait_done chan error
|
var wait_done chan error
|
||||||
var remove_err error
|
|
||||||
var exists bool
|
var exists bool
|
||||||
var wait_err error
|
var wait_err error
|
||||||
|
var lock_err error
|
||||||
|
|
||||||
dir = t.TempDir()
|
dir = t.TempDir()
|
||||||
|
|
||||||
lock_dir = filepath.Join(dir, ".repodata")
|
lock_handle, tmp_dir, err = rpm_lock_repo(dir, RpmLockWait)
|
||||||
err = os.Mkdir(lock_dir, 0775)
|
|
||||||
if err != nil {
|
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)
|
_, _, err = rpm_lock_repo(dir, RpmLockFail)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@@ -187,23 +187,25 @@ func TestRpmLockRepoModes(t *testing.T) {
|
|||||||
|
|
||||||
wait_done = make(chan error, 1)
|
wait_done = make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
var tmp_lock string
|
var tmp_lock *os.File
|
||||||
var tmp_out string
|
var tmp_out string
|
||||||
var inner_err error
|
var inner_err error
|
||||||
|
|
||||||
tmp_lock, tmp_out, inner_err = rpm_lock_repo(dir, RpmLockWait)
|
tmp_lock, tmp_out, inner_err = rpm_lock_repo(dir, RpmLockWait)
|
||||||
if inner_err == nil {
|
if inner_err == nil {
|
||||||
if tmp_lock == "" || tmp_out == "" {
|
if tmp_lock == nil || tmp_out == "" {
|
||||||
inner_err = fmt.Errorf("RpmLockWait returned empty paths")
|
inner_err = fmt.Errorf("RpmLockWait returned empty paths")
|
||||||
}
|
}
|
||||||
|
_ = rpm_unlock_repo(tmp_lock)
|
||||||
|
rpm_exit_cleanup(tmp_out)
|
||||||
}
|
}
|
||||||
wait_done <- inner_err
|
wait_done <- inner_err
|
||||||
}()
|
}()
|
||||||
|
|
||||||
time.Sleep(150 * time.Millisecond)
|
time.Sleep(150 * time.Millisecond)
|
||||||
remove_err = os.RemoveAll(lock_dir)
|
lock_err = rpm_unlock_repo(lock_handle)
|
||||||
if remove_err != nil {
|
if lock_err != nil {
|
||||||
t.Fatalf("remove lock dir: %v", remove_err)
|
t.Fatalf("unlock lock handle: %v", lock_err)
|
||||||
}
|
}
|
||||||
|
|
||||||
wait_err = <-wait_done
|
wait_err = <-wait_done
|
||||||
@@ -211,12 +213,12 @@ func TestRpmLockRepoModes(t *testing.T) {
|
|||||||
t.Fatalf("RpmLockWait error: %v", wait_err)
|
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 {
|
if err != nil {
|
||||||
t.Fatalf("RpmLockNone error: %v", err)
|
t.Fatalf("RpmLockNone error: %v", err)
|
||||||
}
|
}
|
||||||
if lock_dir != "" {
|
if lock_handle != nil {
|
||||||
t.Fatalf("RpmLockNone should not return lock dir, got %q", lock_dir)
|
t.Fatalf("RpmLockNone should not return lock handle")
|
||||||
}
|
}
|
||||||
exists, err = rpm_path_exists(tmp_dir)
|
exists, err = rpm_path_exists(tmp_dir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -225,4 +227,46 @@ func TestRpmLockRepoModes(t *testing.T) {
|
|||||||
if !exists {
|
if !exists {
|
||||||
t.Fatalf("RpmLockNone tmp dir missing: %q", tmp_dir)
|
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