5 Commits

Author SHA1 Message Date
c9c8397856 Исправлена проблема, когда при первом запуске ALR требовалось вручную
All checks were successful
Pre-commit / pre-commit (push) Successful in 6m30s
Create Release / changelog (push) Successful in 3m31s
выполнять 'sudo alr fix' для создания необходимых директорий. Теперь
  директории /var/cache/alr и /tmp/alr создаются автоматически при первом
  использовании с правильными правами доступа.
2025-11-23 15:16:22 +03:00
107075e8ef Исправлен dlcache_prod
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m46s
2025-10-12 19:11:15 +03:00
41e3d8119f Добавлены files-find: systemd, systemd-user, license
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m33s
Create Release / changelog (push) Successful in 3m12s
2025-09-25 22:10:47 +03:00
cf804ec66b Исправлена проблема с перемещением готового пакета из временной дирректории сборки (в случае зависимости)
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m12s
Create Release / changelog (push) Successful in 3m6s
2025-09-21 17:50:31 +03:00
6773d51caf Добавление функций обработки files
All checks were successful
Pre-commit / pre-commit (push) Successful in 5m8s
Create Release / changelog (push) Successful in 3m5s
2025-09-21 16:42:04 +03:00
11 changed files with 512 additions and 98 deletions

3
.gitignore vendored
View File

@@ -12,3 +12,6 @@
e2e-tests/alr e2e-tests/alr
CLAUDE.md CLAUDE.md
commit_msg.txt commit_msg.txt
/scripts/.claude/settings.local.json
/ALR
.claude/settings.local.json

View File

@@ -197,6 +197,13 @@ func BuildCmd() *cli.Command {
for _, pkg := range res { for _, pkg := range res {
name := filepath.Base(pkg.Path) name := filepath.Base(pkg.Path)
// Проверяем, существует ли файл перед перемещением
if _, err := os.Stat(pkg.Path); os.IsNotExist(err) {
slog.Info("Package file already moved or removed, skipping", "path", pkg.Path)
continue
}
err = osutils.Move(pkg.Path, filepath.Join(wd, name)) err = osutils.Move(pkg.Path, filepath.Join(wd, name))
if err != nil { if err != nil {
return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err) return cliutils.FormatCliExit(gotext.Get("Error moving the package"), err)

View File

@@ -167,15 +167,30 @@ func (e *LocalScriptExecutor) ExecuteSecondPass(
pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета pkgName := packager.ConventionalFileName(pkgInfo) // Получаем имя файла пакета
pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету pkgPath := filepath.Join(dirs.BaseDir, pkgName) // Определяем путь к пакету
slog.Info("Creating package file", "path", pkgPath, "name", pkgName)
pkgFile, err := os.Create(pkgPath) pkgFile, err := os.Create(pkgPath)
if err != nil { if err != nil {
slog.Error("Failed to create package file", "path", pkgPath, "error", err)
return nil, err
}
defer pkgFile.Close()
slog.Info("Packaging with nfpm", "format", pkgFormat)
err = packager.Package(pkgInfo, pkgFile)
if err != nil {
slog.Error("Failed to create package", "path", pkgPath, "error", err)
return nil, err return nil, err
} }
err = packager.Package(pkgInfo, pkgFile) slog.Info("Package created successfully", "path", pkgPath)
if err != nil {
// Проверяем, что файл действительно существует
if _, err := os.Stat(pkgPath); err != nil {
slog.Error("Package file not found after creation", "path", pkgPath, "error", err)
return nil, err return nil, err
} }
slog.Info("Package file verified to exist", "path", pkgPath)
builtDeps = append(builtDeps, &BuiltDep{ builtDeps = append(builtDeps, &BuiltDep{
Name: vars.Name, Name: vars.Name,

View File

@@ -49,12 +49,15 @@ import (
// Функция prepareDirs подготавливает директории для сборки. // Функция prepareDirs подготавливает директории для сборки.
func prepareDirs(dirs types.Directories) error { func prepareDirs(dirs types.Directories) error {
// Пробуем удалить базовую директорию, если она существует // Удаляем только директории источников и упаковки, не трогаем файлы пакетов в BaseDir
err := os.RemoveAll(dirs.BaseDir) err := os.RemoveAll(dirs.SrcDir)
if err != nil { if err != nil {
// Если не можем удалить (например, принадлежит root), логируем и продолжаем slog.Debug("Failed to remove src directory", "path", dirs.SrcDir, "error", err)
// Новые директории будут созданы или перезаписаны }
slog.Debug("Failed to remove base directory", "path", dirs.BaseDir, "error", err)
err = os.RemoveAll(dirs.PkgDir)
if err != nil {
slog.Debug("Failed to remove pkg directory", "path", dirs.PkgDir, "error", err)
} }
// Создаем базовую директорию для пакета с setgid битом // Создаем базовую директорию для пакета с setgid битом

View File

@@ -31,6 +31,7 @@ import (
"xorm.io/xorm" "xorm.io/xorm"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/config" "gitea.plemya-x.ru/Plemya-x/ALR/internal/config"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/fsutils"
"gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh" "gitea.plemya-x.ru/Plemya-x/ALR/pkg/alrsh"
) )
@@ -62,9 +63,11 @@ func (d *Database) Connect() error {
dbDir := filepath.Dir(dsn) dbDir := filepath.Dir(dsn)
if _, err := os.Stat(dbDir); err != nil { if _, err := os.Stat(dbDir); err != nil {
if os.IsNotExist(err) { if os.IsNotExist(err) {
// Директория не существует - не пытаемся создать // Директория не существует - создаем автоматически
// Пользователь должен использовать alr fix для создания системных каталогов slog.Info(gotext.Get("Cache directory does not exist, creating it"))
return fmt.Errorf("cache directory does not exist, please run 'sudo alr fix' to create it") if err := fsutils.EnsureTempDirWithRootOwner(dbDir, 0o2775); err != nil {
return fmt.Errorf("failed to create cache directory: %w", err)
}
} else { } else {
return fmt.Errorf("failed to check database directory: %w", err) return fmt.Errorf("failed to check database directory: %w", err)
} }

100
internal/fsutils/dirs.go Normal file
View File

@@ -0,0 +1,100 @@
// ALR - Any Linux Repository
// Copyright (C) 2025 The ALR Authors
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package fsutils
import (
"fmt"
"os"
"os/exec"
"strings"
)
// EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы
// Все каталоги в /tmp/alr и /var/cache/alr принадлежат root:привилегированная_группа с правами 2775
// Для других каталогов использует стандартные права
func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error {
needsElevation := strings.HasPrefix(path, "/tmp/alr") || strings.HasPrefix(path, "/var/cache/alr")
if needsElevation {
// В CI или если мы уже root, не нужно использовать sudo
isRoot := os.Geteuid() == 0
isCI := os.Getenv("CI") == "true"
// В CI создаем директории с обычными правами
if isCI {
err := os.MkdirAll(path, mode)
if err != nil {
return err
}
// В CI не используем группу wheel и не меняем права
// Устанавливаем базовые права 777 для временных каталогов
chmodCmd := exec.Command("chmod", "777", path)
chmodCmd.Run() // Игнорируем ошибки
return nil
}
// Для обычной работы устанавливаем права и привилегированную группу
permissions := "2775"
group := GetPrivilegedGroup()
var mkdirCmd, chmodCmd, chownCmd *exec.Cmd
if isRoot {
// Выполняем команды напрямую без sudo
mkdirCmd = exec.Command("mkdir", "-p", path)
chmodCmd = exec.Command("chmod", permissions, path)
chownCmd = exec.Command("chown", "root:"+group, path)
} else {
// Используем sudo для всех операций с привилегированными каталогами
mkdirCmd = exec.Command("sudo", "mkdir", "-p", path)
chmodCmd = exec.Command("sudo", "chmod", permissions, path)
chownCmd = exec.Command("sudo", "chown", "root:"+group, path)
}
// Создаем директорию через sudo если нужно
err := mkdirCmd.Run()
if err != nil {
// Игнорируем ошибку если директория уже существует
if !isRoot {
// Проверяем существует ли директория
if _, statErr := os.Stat(path); statErr != nil {
return fmt.Errorf("не удалось создать директорию %s: %w", path, err)
}
}
}
// Устанавливаем права с setgid битом для наследования группы
err = chmodCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить права на %s: %w", path, err)
}
}
// Устанавливаем владельца root:группа
err = chownCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить владельца на %s: %w", path, err)
}
}
return nil
}
// Для остальных каталогов обычное создание
return os.MkdirAll(path, mode)
}

View File

@@ -14,7 +14,7 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
package utils package fsutils
import ( import (
"context" "context"

View File

@@ -177,3 +177,333 @@ func filesFindCmd(hc interp.HandlerContext, cmd string, args []string) error {
return outputFiles(hc, foundFiles) return outputFiles(hc, foundFiles)
} }
func filesFindBinCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
binPath := "./usr/bin/"
realPath := path.Join(hc.Dir, binPath)
if err := validateDir(realPath, "files-find-bin"); err != nil {
return err
}
var binFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
binFiles = append(binFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-bin: %w", err)
}
return outputFiles(hc, binFiles)
}
func filesFindLibCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
libPaths := []string{"./usr/lib/", "./usr/lib64/"}
var libFiles []string
for _, libPath := range libPaths {
realPath := path.Join(hc.Dir, libPath)
if _, err := os.Stat(realPath); os.IsNotExist(err) {
continue
}
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
libFiles = append(libFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-lib: %w", err)
}
}
return outputFiles(hc, libFiles)
}
func filesFindIncludeCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
includePath := "./usr/include/"
realPath := path.Join(hc.Dir, includePath)
if err := validateDir(realPath, "files-find-include"); err != nil {
return err
}
var includeFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
includeFiles = append(includeFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-include: %w", err)
}
return outputFiles(hc, includeFiles)
}
func filesFindShareCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
sharePath := "./usr/share/"
if len(args) > 0 {
if len(args) == 1 {
sharePath = "./usr/share/" + args[0] + "/"
} else {
sharePath = "./usr/share/" + args[0] + "/"
namePattern = args[1]
}
}
realPath := path.Join(hc.Dir, sharePath)
if err := validateDir(realPath, "files-find-share"); err != nil {
return err
}
var shareFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
shareFiles = append(shareFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-share: %w", err)
}
return outputFiles(hc, shareFiles)
}
func filesFindManCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
manSection := "*"
if len(args) > 0 {
if len(args) == 1 {
manSection = args[0]
} else {
manSection = args[0]
namePattern = args[1]
}
}
manPath := "./usr/share/man/man" + manSection + "/"
realPath := path.Join(hc.Dir, manPath)
if err := validateDir(realPath, "files-find-man"); err != nil {
return err
}
var manFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
manFiles = append(manFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-man: %w", err)
}
return outputFiles(hc, manFiles)
}
func filesFindConfigCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
configPath := "./etc/"
realPath := path.Join(hc.Dir, configPath)
if err := validateDir(realPath, "files-find-config"); err != nil {
return err
}
var configFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
configFiles = append(configFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-config: %w", err)
}
return outputFiles(hc, configFiles)
}
func filesFindSystemdCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
systemdPath := "./usr/lib/systemd/system/"
realPath := path.Join(hc.Dir, systemdPath)
if err := validateDir(realPath, "files-find-systemd"); err != nil {
return err
}
var systemdFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
systemdFiles = append(systemdFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-systemd: %w", err)
}
return outputFiles(hc, systemdFiles)
}
func filesFindSystemdUserCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
systemdUserPath := "./usr/lib/systemd/user/"
realPath := path.Join(hc.Dir, systemdUserPath)
if err := validateDir(realPath, "files-find-systemd-user"); err != nil {
return err
}
var systemdUserFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
systemdUserFiles = append(systemdUserFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-systemd-user: %w", err)
}
return outputFiles(hc, systemdUserFiles)
}
func filesFindLicenseCmd(hc interp.HandlerContext, cmd string, args []string) error {
namePattern := "*"
if len(args) > 0 {
namePattern = args[0]
}
licensePath := "./usr/share/licenses/"
realPath := path.Join(hc.Dir, licensePath)
if err := validateDir(realPath, "files-find-license"); err != nil {
return err
}
var licenseFiles []string
err := filepath.Walk(realPath, func(p string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if !info.IsDir() && matchNamePattern(info.Name(), namePattern) {
relPath, relErr := makeRelativePath(hc.Dir, p)
if relErr != nil {
return relErr
}
licenseFiles = append(licenseFiles, relPath)
}
return nil
})
if err != nil {
return fmt.Errorf("files-find-license: %w", err)
}
return outputFiles(hc, licenseFiles)
}

View File

@@ -59,6 +59,15 @@ var Helpers = handlers.ExecFuncs{
"files-find": filesFindCmd, "files-find": filesFindCmd,
"files-find-lang": filesFindLangCmd, "files-find-lang": filesFindLangCmd,
"files-find-doc": filesFindDocCmd, "files-find-doc": filesFindDocCmd,
"files-find-bin": filesFindBinCmd,
"files-find-lib": filesFindLibCmd,
"files-find-include": filesFindIncludeCmd,
"files-find-share": filesFindShareCmd,
"files-find-man": filesFindManCmd,
"files-find-config": filesFindConfigCmd,
"files-find-systemd": filesFindSystemdCmd,
"files-find-systemd-user": filesFindSystemdUserCmd,
"files-find-license": filesFindLicenseCmd,
} }
// Restricted contains restricted read-only helper commands // Restricted contains restricted read-only helper commands
@@ -68,6 +77,15 @@ var Restricted = handlers.ExecFuncs{
"files-find": filesFindCmd, "files-find": filesFindCmd,
"files-find-lang": filesFindLangCmd, "files-find-lang": filesFindLangCmd,
"files-find-doc": filesFindDocCmd, "files-find-doc": filesFindDocCmd,
"files-find-bin": filesFindBinCmd,
"files-find-lib": filesFindLibCmd,
"files-find-include": filesFindIncludeCmd,
"files-find-share": filesFindShareCmd,
"files-find-man": filesFindManCmd,
"files-find-config": filesFindConfigCmd,
"files-find-systemd": filesFindSystemdCmd,
"files-find-systemd-user": filesFindSystemdUserCmd,
"files-find-license": filesFindLicenseCmd,
} }
func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc { func installHelperCmd(prefix string, perms os.FileMode) handlers.ExecFunc {

View File

@@ -19,10 +19,9 @@ package utils
import ( import (
"fmt" "fmt"
"os" "os"
"os/exec"
"os/user" "os/user"
"strings"
"gitea.plemya-x.ru/Plemya-x/ALR/internal/fsutils"
"golang.org/x/sys/unix" "golang.org/x/sys/unix"
) )
@@ -31,79 +30,15 @@ func NoNewPrivs() error {
} }
// EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы // EnsureTempDirWithRootOwner создает каталог в /tmp/alr или /var/cache/alr с правами для привилегированной группы
// Все каталоги в /tmp/alr и /var/cache/alr принадлежат root:привилегированная_группа с правами 2775 // Обёртка для обратной совместимости, делегирует вызов в fsutils
// Для других каталогов использует стандартные права
func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error { func EnsureTempDirWithRootOwner(path string, mode os.FileMode) error {
needsElevation := strings.HasPrefix(path, "/tmp/alr") || strings.HasPrefix(path, "/var/cache/alr") return fsutils.EnsureTempDirWithRootOwner(path, mode)
}
if needsElevation { // GetPrivilegedGroup возвращает привилегированную группу для текущей системы
// В CI или если мы уже root, не нужно использовать sudo // Обёртка для обратной совместимости, делегирует вызов в fsutils
isRoot := os.Geteuid() == 0 func GetPrivilegedGroup() string {
isCI := os.Getenv("CI") == "true" return fsutils.GetPrivilegedGroup()
// В CI создаем директории с обычными правами
if isCI {
err := os.MkdirAll(path, mode)
if err != nil {
return err
}
// В CI не используем группу wheel и не меняем права
// Устанавливаем базовые права 777 для временных каталогов
chmodCmd := exec.Command("chmod", "777", path)
chmodCmd.Run() // Игнорируем ошибки
return nil
}
// Для обычной работы устанавливаем права и привилегированную группу
permissions := "2775"
group := GetPrivilegedGroup()
var mkdirCmd, chmodCmd, chownCmd *exec.Cmd
if isRoot {
// Выполняем команды напрямую без sudo
mkdirCmd = exec.Command("mkdir", "-p", path)
chmodCmd = exec.Command("chmod", permissions, path)
chownCmd = exec.Command("chown", "root:"+group, path)
} else {
// Используем sudo для всех операций с привилегированными каталогами
mkdirCmd = exec.Command("sudo", "mkdir", "-p", path)
chmodCmd = exec.Command("sudo", "chmod", permissions, path)
chownCmd = exec.Command("sudo", "chown", "root:"+group, path)
}
// Создаем директорию через sudo если нужно
err := mkdirCmd.Run()
if err != nil {
// Игнорируем ошибку если директория уже существует
if !isRoot {
// Проверяем существует ли директория
if _, statErr := os.Stat(path); statErr != nil {
return fmt.Errorf("не удалось создать директорию %s: %w", path, err)
}
}
}
// Устанавливаем права с setgid битом для наследования группы
err = chmodCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить права на %s: %w", path, err)
}
}
// Устанавливаем владельца root:группа
err = chownCmd.Run()
if err != nil {
if !isRoot {
return fmt.Errorf("не удалось установить владельца на %s: %w", path, err)
}
}
return nil
}
// Для остальных каталогов обычное создание
return os.MkdirAll(path, mode)
} }
// IsUserInGroup проверяет, состоит ли пользователь в указанной группе // IsUserInGroup проверяет, состоит ли пользователь в указанной группе
@@ -149,7 +84,7 @@ func CheckUserPrivileges() error {
return fmt.Errorf("не удалось получить информацию о текущем пользователе: %w", err) return fmt.Errorf("не удалось получить информацию о текущем пользователе: %w", err)
} }
privilegedGroup := GetPrivilegedGroup() privilegedGroup := fsutils.GetPrivilegedGroup()
// Проверяем членство в привилегированной группе // Проверяем членство в привилегированной группе
if !IsUserInGroup(currentUser.Username, privilegedGroup) { if !IsUserInGroup(currentUser.Username, privilegedGroup) {

View File

@@ -27,9 +27,9 @@ import (
// createDir создает директорию с правильными правами для production // createDir создает директорию с правильными правами для production
func createDir(itemPath string, mode os.FileMode) error { func createDir(itemPath string, mode os.FileMode) error {
// Используем специальную функцию для создания каталогов с setgid битом только для /tmp/alr // Используем специальную функцию для создания каталогов с setgid битом только для /tmp/alr/ и /var/cache/alr/
// В остальных случаях используем обычное создание директории // Проверяем с слешем в конце, чтобы исключить тестовые директории вроде /tmp/alr-test-XXX
if strings.HasPrefix(itemPath, "/tmp/alr") { if strings.HasPrefix(itemPath, "/tmp/alr/") || strings.HasPrefix(itemPath, "/var/cache/alr/") {
return utils.EnsureTempDirWithRootOwner(itemPath, mode) return utils.EnsureTempDirWithRootOwner(itemPath, mode)
} else { } else {
return os.MkdirAll(itemPath, mode) return os.MkdirAll(itemPath, mode)