diff --git a/cmd/bootstrap.go b/cmd/bootstrap.go index c87d4a3..ab0a395 100644 --- a/cmd/bootstrap.go +++ b/cmd/bootstrap.go @@ -72,7 +72,7 @@ var errBootstrapCreateFile = exit.Error{ func (bs cBootstrap) Run(context wisski_distillery.Context) error { // installation environment is the native environment! // TODO: Should this be configurable? - env := new(environment.Native) + var env environment.Environment root := bs.Directory @@ -86,7 +86,7 @@ func (bs cBootstrap) Run(context wisski_distillery.Context) error { { logging.LogMessage(context.Stderr, context.Context, "Creating root deployment directory") - if err := env.MkdirAll(root, environment.DefaultDirPerm); err != nil { + if err := fsx.MkdirAll(root, fsx.DefaultDirPerm); err != nil { return errBootstrapFailedToCreateDirectory.WithMessageF(root) } if err := cli.WriteBaseDirectory(env, root); err != nil { @@ -116,7 +116,7 @@ func (bs cBootstrap) Run(context wisski_distillery.Context) error { return errBoostrapFailedToCopyExe.WithMessageF(err) } - err = fsx.CopyFile(context.Context, env, wdcliPath, exe) + err = fsx.CopyFile(context.Context, wdcliPath, exe) if err != nil && err != fsx.ErrCopySameFile { return errBoostrapFailedToCopyExe.WithMessageF(err) } @@ -124,15 +124,14 @@ func (bs cBootstrap) Run(context wisski_distillery.Context) error { } { - if !fsx.IsFile(env, cfgPath) { + if !fsx.IsFile(cfgPath) { // generate the configuration from the template cfg := tpl.Generate() // write out all the extra config files if err := logging.LogOperation(func() error { context.Println(cfg.Paths.OverridesJSON) - if err := environment.WriteFile( - env, + if err := fsx.WriteFile( cfg.Paths.OverridesJSON, bootstrap.DefaultOverridesJSON, fs.ModePerm, @@ -141,8 +140,7 @@ func (bs cBootstrap) Run(context wisski_distillery.Context) error { } context.Println(cfg.Paths.ResolverBlocks) - if err := environment.WriteFile( - env, + if err := fsx.WriteFile( cfg.Paths.ResolverBlocks, bootstrap.DefaultResolverBlockedTXT, fs.ModePerm, @@ -162,7 +160,7 @@ func (bs cBootstrap) Run(context wisski_distillery.Context) error { // and marshal it out! if err := logging.LogOperation(func() error { - configYML, err := env.Create(cfgPath, environment.DefaultFilePerm) + configYML, err := fsx.Create(cfgPath, fsx.DefaultFilePerm) if err != nil { return err } diff --git a/cmd/config_migrate.go b/cmd/config_migrate.go index a052f28..241d462 100644 --- a/cmd/config_migrate.go +++ b/cmd/config_migrate.go @@ -32,7 +32,7 @@ func (cfgMigrate) Description() wisski_distillery.Description { func (c cfgMigrate) Run(context wisski_distillery.Context) error { // migration environment is the native environment! - env := new(environment.Native) + var env environment.Environment // open the legacy file file, err := os.Open(c.Positionals.Input) diff --git a/cmd/monday.go b/cmd/monday.go index 5f2f500..c32bcb6 100644 --- a/cmd/monday.go +++ b/cmd/monday.go @@ -3,7 +3,6 @@ package cmd import ( wisski_distillery "github.com/FAU-CDI/wisski-distillery" "github.com/FAU-CDI/wisski-distillery/internal/cli" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/FAU-CDI/wisski-distillery/pkg/logging" ) @@ -29,8 +28,7 @@ func (monday) Description() wisski_distillery.Description { } func (monday monday) AfterParse() error { - // TODO: Use a generic environment here! - if !fsx.IsFile(new(environment.Native), monday.Positionals.GraphdbZip) { + if !fsx.IsFile(monday.Positionals.GraphdbZip) { return errNoGraphDBZip.WithMessageF(monday.Positionals.GraphdbZip) } return nil diff --git a/cmd/reserve.go b/cmd/reserve.go index fa92994..f9aca0f 100644 --- a/cmd/reserve.go +++ b/cmd/reserve.go @@ -58,7 +58,7 @@ func (r reserve) Run(context wisski_distillery.Context) error { // check that the base directory does not exist logging.LogMessage(context.Stderr, context.Context, "Checking that base directory %s does not exist", instance.FilesystemBase) - if fsx.IsDirectory(dis.Environment, instance.FilesystemBase) { + if fsx.IsDirectory(instance.FilesystemBase) { return errReserveAlreadyExists.WithMessageF(slug) } diff --git a/cmd/system_update.go b/cmd/system_update.go index 0c301e1..19e293b 100644 --- a/cmd/system_update.go +++ b/cmd/system_update.go @@ -8,7 +8,6 @@ import ( wisski_distillery "github.com/FAU-CDI/wisski-distillery" "github.com/FAU-CDI/wisski-distillery/internal/cli" "github.com/FAU-CDI/wisski-distillery/internal/dis/component" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" "github.com/FAU-CDI/wisski-distillery/pkg/execx" "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/FAU-CDI/wisski-distillery/pkg/logging" @@ -47,7 +46,7 @@ var errNoGraphDBZip = exit.Error{ func (s systemupdate) AfterParse() error { // TODO: Use a generic environment here! - if !fsx.IsFile(new(environment.Native), s.Positionals.GraphdbZip) { + if !fsx.IsFile(s.Positionals.GraphdbZip) { return errNoGraphDBZip.WithMessageF(s.Positionals.GraphdbZip) } return nil @@ -76,7 +75,7 @@ func (si systemupdate) Run(context wisski_distillery.Context) error { dis.Templating().CustomAssetsPath(), } { context.Println(d) - if err := dis.Still.Environment.MkdirAll(d, environment.DefaultDirPerm); err != nil { + if err := fsx.MkdirAll(d, fsx.DefaultDirPerm); err != nil { return errBoostrapFailedToCreateDirectory.WithMessageF(d, err) } } diff --git a/internal/config/legacy/stringparser/stringparser.go b/internal/config/legacy/stringparser/stringparser.go index 5d73a3d..bc44c11 100644 --- a/internal/config/legacy/stringparser/stringparser.go +++ b/internal/config/legacy/stringparser/stringparser.go @@ -24,7 +24,7 @@ type Parser[T any] func(env environment.Environment, s string) (T, error) // ParseAbspath checks that s is an absolute path and returns it as-is func ParseAbspath(env environment.Environment, s string) (string, error) { - if !fsx.IsDirectory(env, s) { + if !fsx.IsDirectory(s) { return "", errors.Errorf("%q does not exist or is not a directory", s) } return s, nil @@ -32,7 +32,7 @@ func ParseAbspath(env environment.Environment, s string) (string, error) { // ParseFile checks that s is a valid file and returns it as-is func ParseFile(env environment.Environment, s string) (string, error) { - if !fsx.IsFile(env, s) { + if !fsx.IsFile(s) { return "", errors.Errorf("%q does not exist or is not a regular file", s) } return s, nil diff --git a/internal/config/paths.go b/internal/config/paths.go index 24f9226..7b6f84d 100644 --- a/internal/config/paths.go +++ b/internal/config/paths.go @@ -39,14 +39,14 @@ func (pcfg PathsConfig) UsingDistilleryExecutable(env environment.Environment) b if err != nil { return false } - return fsx.SameFile(env, exe, pcfg.ExecutablePath()) + return fsx.SameFile(exe, pcfg.ExecutablePath()) } // CurrentExecutable returns the path to the current executable being used. // When it does not exist, falls back to the default executable. func (pcfg PathsConfig) CurrentExecutable(env environment.Environment) string { exe, err := os.Executable() - if err != nil || !fsx.IsFile(env, exe) { + if err != nil || !fsx.IsFile(exe) { return pcfg.ExecutablePath() } return exe diff --git a/internal/config/validators/files.go b/internal/config/validators/files.go index e189659..2fa3d84 100644 --- a/internal/config/validators/files.go +++ b/internal/config/validators/files.go @@ -10,7 +10,7 @@ func ValidateFile(env environment.Environment, path *string, dflt string) error if *path == "" { *path = dflt } - if !fsx.IsFile(env, *path) { + if !fsx.IsFile(*path) { return errors.Errorf("%q does not exist or is not a file", *path) } return nil @@ -20,7 +20,7 @@ func ValidateDirectory(env environment.Environment, path *string, dflt string) e if *path == "" { *path = dflt } - if !fsx.IsDirectory(env, *path) { + if !fsx.IsDirectory(*path) { return errors.Errorf("%q does not exist or is not a directory", *path) } return nil diff --git a/internal/dis/component/backup.go b/internal/dis/component/backup.go index ffc954a..bb5e74e 100644 --- a/internal/dis/component/backup.go +++ b/internal/dis/component/backup.go @@ -147,7 +147,7 @@ func (sc *stagingContext) CopyFile(dst, src string) error { return err } sc.sendPath(dst) - return fsx.CopyFile(sc.ctx, sc.env, dstPath, src) + return fsx.CopyFile(sc.ctx, dstPath, src) } func (sc *stagingContext) CopyDirectory(dst, src string) error { @@ -160,7 +160,7 @@ func (sc *stagingContext) CopyDirectory(dst, src string) error { return err } - return fsx.CopyDirectory(sc.ctx, sc.env, dstPath, src, func(dst, src string) { + return fsx.CopyDirectory(sc.ctx, dstPath, src, func(dst, src string) { sc.sendPath(dst) }) } diff --git a/internal/dis/component/exporter/backup.go b/internal/dis/component/exporter/backup.go index 4ce3146..8b67b89 100644 --- a/internal/dis/component/exporter/backup.go +++ b/internal/dis/component/exporter/backup.go @@ -10,7 +10,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/internal/wisski" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" + "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/tkw1536/goprogram/status" "golang.org/x/exp/slices" @@ -119,7 +119,7 @@ func (backup *Backup) run(ctx context.Context, progress io.Writer, exporter *Exp defer st.Stop() instancesBackupDir := filepath.Join(backup.Description.Dest, "instances") - if err := exporter.Environment.Mkdir(instancesBackupDir, environment.DefaultDirPerm); err != nil { + if err := fsx.Mkdir(instancesBackupDir, fsx.DefaultDirPerm); err != nil { backup.InstanceListErr = err return nil } @@ -140,7 +140,7 @@ func (backup *Backup) run(ctx context.Context, progress io.Writer, exporter *Exp Handler: func(instance *wisski.WissKI, index int, writer io.Writer) Snapshot { dir := filepath.Join(instancesBackupDir, instance.Slug) - if err := exporter.Environment.Mkdir(dir, environment.DefaultDirPerm); err != nil { + if err := fsx.Mkdir(dir, fsx.DefaultDirPerm); err != nil { return Snapshot{ ErrPanic: err, } diff --git a/internal/dis/component/exporter/exporter.go b/internal/dis/component/exporter/exporter.go index 4ad08d1..8b58ce2 100644 --- a/internal/dis/component/exporter/exporter.go +++ b/internal/dis/component/exporter/exporter.go @@ -12,7 +12,6 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances" "github.com/FAU-CDI/wisski-distillery/internal/dis/component/sql" "github.com/FAU-CDI/wisski-distillery/internal/passwordx" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/tkw1536/pkglib/password" ) @@ -51,7 +50,7 @@ func (dis *Exporter) ArchivePath() string { // The path is guaranteed to not exist. func (dis *Exporter) NewArchivePath(prefix string) (path string) { // TODO: Consider moving these into a subdirectory with the provided prefix. - for path == "" || fsx.Exists(dis.Environment, path) { + for path == "" || fsx.Exists(path) { name := dis.newSnapshotName(prefix) + ".tar.gz" path = filepath.Join(dis.ArchivePath(), name) } @@ -75,7 +74,7 @@ func (*Exporter) newSnapshotName(prefix string) string { func (dis *Exporter) NewStagingDir(prefix string) (path string, err error) { for path == "" || os.IsExist(err) { path = filepath.Join(dis.StagingPath(), dis.newSnapshotName(prefix)) - err = dis.Still.Environment.Mkdir(path, environment.DefaultFilePerm) + err = fsx.Mkdir(path, fsx.DefaultFilePerm) } if err != nil { path = "" diff --git a/internal/dis/component/exporter/iface.go b/internal/dis/component/exporter/iface.go index 38fbfac..226e247 100644 --- a/internal/dis/component/exporter/iface.go +++ b/internal/dis/component/exporter/iface.go @@ -8,7 +8,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/models" "github.com/FAU-CDI/wisski-distillery/internal/wisski" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" + "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/FAU-CDI/wisski-distillery/pkg/targz" "github.com/tkw1536/goprogram/status" @@ -74,7 +74,7 @@ func (exporter *Exporter) MakeExport(ctx context.Context, progress io.Writer, ta // create the staging directory logging.LogMessage(progress, ctx, "Creating staging directory") - err = exporter.Environment.Mkdir(stagingDir, environment.DefaultDirPerm) + err = fsx.Mkdir(stagingDir, fsx.DefaultDirPerm) if !os.IsExist(err) && err != nil { return err } @@ -112,7 +112,7 @@ func (exporter *Exporter) MakeExport(ctx context.Context, progress io.Writer, ta logging.ProgressF(progress, ctx, reportPath) // create the path - report, err := exporter.Environment.Create(reportPath, environment.DefaultFilePerm) + report, err := fsx.Create(reportPath, fsx.DefaultFilePerm) if err != nil { return err } diff --git a/internal/dis/component/provision/provision.go b/internal/dis/component/provision/provision.go index 6553e9c..7e1363b 100644 --- a/internal/dis/component/provision/provision.go +++ b/internal/dis/component/provision/provision.go @@ -43,7 +43,7 @@ func (pv *Provision) Provision(progress io.Writer, ctx context.Context, flags Pr // check that the base directory does not exist logging.LogMessage(progress, ctx, "Checking that base directory %s does not exist", instance.FilesystemBase) - if fsx.IsDirectory(pv.Environment, instance.FilesystemBase) { + if fsx.IsDirectory(instance.FilesystemBase) { return nil, ErrInstanceAlreadyExists } diff --git a/internal/dis/component/sql/sql.go b/internal/dis/component/sql/sql.go index 45554fa..50ff1c8 100644 --- a/internal/dis/component/sql/sql.go +++ b/internal/dis/component/sql/sql.go @@ -7,6 +7,7 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/dis/component" "github.com/FAU-CDI/wisski-distillery/pkg/environment" + "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/tkw1536/pkglib/lazy" ) @@ -53,7 +54,7 @@ func (sql *SQL) Stack(env environment.Environment) component.StackWithResources "HTTPS_ENABLED": sql.Config.HTTP.HTTPSEnabledEnv(), }, - MakeDirsPerm: environment.DefaultDirPerm, + MakeDirsPerm: fsx.DefaultDirPerm, MakeDirs: []string{ "data", "imports", diff --git a/internal/dis/component/ssh2/server_hostkeys.go b/internal/dis/component/ssh2/server_hostkeys.go index 0c20b44..5eefddc 100644 --- a/internal/dis/component/ssh2/server_hostkeys.go +++ b/internal/dis/component/ssh2/server_hostkeys.go @@ -10,7 +10,7 @@ import ( "io" "os" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" + "github.com/FAU-CDI/wisski-distillery/pkg/fsx" "github.com/FAU-CDI/wisski-distillery/pkg/logging" "github.com/gliderlabs/ssh" @@ -119,7 +119,7 @@ func (ssh2 *SSH2) makeHostKey(progress io.Writer, ctx context.Context, key HostK } // generate and write private key as PEM - privateKeyFile, err := ssh2.Environment.Create(path, environment.DefaultFilePerm) + privateKeyFile, err := fsx.Create(path, fsx.DefaultFilePerm) if err != nil { return err } diff --git a/internal/dis/component/stack.go b/internal/dis/component/stack.go index 95e4b22..aaf92a0 100644 --- a/internal/dis/component/stack.go +++ b/internal/dis/component/stack.go @@ -268,7 +268,7 @@ func (is StackWithResources) Install(ctx context.Context, progress io.Writer, co // copy over file from context logging.ProgressF(progress, ctx, "[copy] %s (from %s)\n", dst, src) - if err := fsx.CopyFile(ctx, env, dst, src); err != nil { + if err := fsx.CopyFile(ctx, dst, src); err != nil { return errors.Wrapf(err, "Unable to copy file %s", src) } } diff --git a/internal/dis/init.go b/internal/dis/init.go index 3cfae81..61f00e3 100644 --- a/internal/dis/init.go +++ b/internal/dis/init.go @@ -5,8 +5,6 @@ import ( "github.com/FAU-CDI/wisski-distillery/internal/cli" "github.com/FAU-CDI/wisski-distillery/internal/config" - "github.com/FAU-CDI/wisski-distillery/internal/dis/component" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" "github.com/tkw1536/goprogram/exit" ) @@ -23,9 +21,6 @@ var errOpenConfig = exit.Error{ // NewDistillery creates a new distillery from the provided flags func NewDistillery(params cli.Params, flags cli.Flags, req cli.Requirements) (dis *Distillery, err error) { dis = &Distillery{ - Still: component.Still{ - Environment: new(environment.Native), - }, Upstream: Upstream{ SQL: "127.0.0.1:3306", Triplestore: "127.0.0.1:7200", diff --git a/internal/wisski/ingredient/php/extras/prefixes.go b/internal/wisski/ingredient/php/extras/prefixes.go index 9b21494..bf81e78 100644 --- a/internal/wisski/ingredient/php/extras/prefixes.go +++ b/internal/wisski/ingredient/php/extras/prefixes.go @@ -34,7 +34,7 @@ var ( // NoPrefix checks if this WissKI instance is excluded from generating prefixes. // TODO: Move this to the database! func (prefixes *Prefixes) NoPrefix() bool { - return fsx.IsFile(prefixes.Malt.Environment, filepath.Join(prefixes.FilesystemBase, "prefixes.skip")) + return fsx.IsFile(filepath.Join(prefixes.FilesystemBase, "prefixes.skip")) } //go:embed prefixes.php @@ -120,7 +120,7 @@ func hasAnyPrefix(candidate string, prefixes []string) bool { func (wisski *Prefixes) filePrefixes() (prefixes []string, err error) { path := filepath.Join(wisski.FilesystemBase, "prefixes") - if !fsx.IsFile(wisski.Malt.Environment, path) { + if !fsx.IsFile(path) { return nil, nil } diff --git a/pkg/fsx/copy.go b/pkg/fsx/copy.go index 9363a98..8869f55 100644 --- a/pkg/fsx/copy.go +++ b/pkg/fsx/copy.go @@ -7,7 +7,6 @@ import ( "os" "path/filepath" - "github.com/FAU-CDI/wisski-distillery/pkg/environment" "github.com/tkw1536/pkglib/contextx" ) @@ -16,16 +15,14 @@ var ErrCopySameFile = errors.New("src and dst must be different") // CopyFile copies a file from src to dst. // When src points to a symbolic link, will copy the symbolic link. // -// When dst and src are the same file, returns ErrCopySameFile. +// When dst and src are the same file, returns [ErrCopySameFile]. // When ctx is closed, the file is not copied. -// -// Ignores the umask. -func CopyFile(ctx context.Context, env environment.Environment, dst, src string) error { +func CopyFile(ctx context.Context, dst, src string) error { if err := ctx.Err(); err != nil { return err } - if SameFile(env, src, dst) { + if SameFile(src, dst) { return ErrCopySameFile } @@ -56,13 +53,13 @@ func CopyFile(ctx context.Context, env environment.Environment, dst, src string) // CopyLink copies a link from src to dst. // If dst already exists, it is deleted and then re-created. -func CopyLink(ctx context.Context, env environment.Environment, dst, src string) error { +func CopyLink(ctx context.Context, dst, src string) error { if err := ctx.Err(); err != nil { return err } // if they're the same file that is an error - if SameFile(env, dst, src) { + if SameFile(dst, src) { return ErrCopySameFile } @@ -73,7 +70,7 @@ func CopyLink(ctx context.Context, env environment.Environment, dst, src string) } // delete it if it already exists - if Exists(env, dst) { + if Exists(dst) { if err := os.Remove(dst); err != nil { return err } @@ -92,12 +89,12 @@ var ErrDstFile = errors.New("dst is a file") // When a directory already exists, additional files are not deleted. // // onCopy, when not nil, is called for each file or directory being copied. -func CopyDirectory(ctx context.Context, env environment.Environment, dst, src string, onCopy func(dst, src string)) error { +func CopyDirectory(ctx context.Context, dst, src string, onCopy func(dst, src string)) error { // sanity checks - if SameFile(env, src, dst) { + if SameFile(src, dst) { return ErrCopySameFile } - if IsFile(env, dst) { + if IsFile(dst) { return ErrDstFile } @@ -133,18 +130,18 @@ func CopyDirectory(ctx context.Context, env environment.Environment, dst, src st // if we have a symbolic link, copy the link! if info.Mode()&fs.ModeSymlink != 0 { - return CopyLink(ctx, env, dst, path) + return CopyLink(ctx, dst, path) } // if we got a file, we should copy it normally if !d.IsDir() { - return CopyFile(ctx, env, dst, path) + return CopyFile(ctx, dst, path) } // create the directory, but ignore an error if the directory already exists. // this is so that we can copy one tree into another tree. err = Mkdir(dst, info.Mode()) - if os.IsExist(err) && IsDirectory(env, dst) { + if os.IsExist(err) && IsDirectory(dst) { err = nil } diff --git a/pkg/fsx/create.go b/pkg/fsx/create.go new file mode 100644 index 0000000..df95847 --- /dev/null +++ b/pkg/fsx/create.go @@ -0,0 +1,56 @@ +package fsx + +import ( + "io/fs" + "os" + "time" +) + +// Create is like [os.Create] with an additional mode argument. +func Create(path string, mode fs.FileMode) (*os.File, error) { + m.Lock() + defer m.Unlock() + + return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode) +} + +// WriteFile is like [os.WriteFile]. +func WriteFile(path string, data []byte, mode fs.FileMode) error { + handle, err := Create(path, mode) + if err != nil { + return err + } + defer handle.Close() + + if _, err := handle.Write(data); err != nil { + return err + } + + return nil +} + +// Touch touches a file. +// It is similar to the unix 'touch' command. +// +// If the file does not exist, it is created using [Create]. +// If the file does exist, its' access and modification times are updated to the current time. +func Touch(path string, perm fs.FileMode) error { + if perm == 0 { + perm = DefaultFilePerm + } + _, err := os.Stat(path) + switch { + case os.IsNotExist(err): + f, err := Create(path, perm) + if err != nil { + return err + } + defer f.Close() + return nil + case err != nil: + return err + default: + now := time.Now().Local() + return os.Chtimes(path, now, now) + } +} diff --git a/pkg/fsx/dir.go b/pkg/fsx/dir.go new file mode 100644 index 0000000..412ad4b --- /dev/null +++ b/pkg/fsx/dir.go @@ -0,0 +1,25 @@ +package fsx + +import ( + "io/fs" + "os" +) + +// DefaultDirPerm should be used by callers to use a consistent mode for new directories. +const DefaultDirPerm fs.FileMode = fs.ModeDir | fs.ModePerm + +// Mkdir is like [os.Mkdir]. +func Mkdir(path string, mode fs.FileMode) error { + m.Lock() + defer m.Unlock() + + return os.Mkdir(path, fs.ModeDir|mode) +} + +// MkdirAll is like [os.MkdirAll]. +func MkdirAll(path string, mode fs.FileMode) error { + m.Lock() + defer m.Unlock() + + return os.MkdirAll(path, fs.ModeDir|mode) +} diff --git a/pkg/fsx/fsx.go b/pkg/fsx/fsx.go index 2c15c2d..6b0cf14 100644 --- a/pkg/fsx/fsx.go +++ b/pkg/fsx/fsx.go @@ -1,16 +1,37 @@ // Package fsx provides additional file system functionality. // -// Several functions in this package provide umask-ignoring functions. -// Using these functions intervenes with the global umask. -// -// It is not safe to use functions provided by the standard go library concurrently with this function. +// All functions in this package ignore the umask. +// As such it is not safe to use otherwise equivalent functions provided by the standard go library concurrently with this package. // Users should take care that no other code in their application uses these functions. package fsx -import "io/fs" +import ( + "io/fs" + "sync" + "syscall" +) // DefaultFilePerm should be used by callers to use a consistent file mode for new files. const DefaultFilePerm fs.FileMode = 0666 -// DefaultDirPerm should be used by callers to use a consistent mode for new directories. -const DefaultDirPerm fs.FileMode = fs.ModeDir | fs.ModePerm +// mask is the global mask lock +var m mask + +// mask allows disabling and re-enabling the global umask. +// it is used by allow functions of this package. +type mask struct { + l sync.Mutex // locked? + umask int // previous mask +} + +// Lock blocks until no other function is using this umask +// and then sets it to 0. +func (mask *mask) Lock() { + mask.l.Lock() + mask.umask = syscall.Umask(0) +} + +func (mask *mask) Unlock() { + mask.umask = syscall.Umask(mask.umask) + mask.l.Unlock() +} diff --git a/pkg/fsx/same.go b/pkg/fsx/same.go index 7721234..197eea4 100644 --- a/pkg/fsx/same.go +++ b/pkg/fsx/same.go @@ -3,17 +3,15 @@ package fsx import ( "os" "path/filepath" - - "github.com/FAU-CDI/wisski-distillery/pkg/environment" ) // SameFile checks if path1 and path2 refer to the same file. -// If both files exist, they are compared using [env.SameFile]. +// If both files exist, they are compared using [os.SameFile]. // If both files do not exist, the paths are first compared syntactically and then via recursion on [filepath.Dir]. -func SameFile(env environment.Environment, path1, path2 string) bool { +func SameFile(path1, path2 string) bool { // initial attempt: check if directly - same, certain := couldBeSameFile(env, path1, path2) + same, certain := couldBeSameFile(path1, path2) if certain { return same } @@ -30,19 +28,19 @@ func SameFile(env environment.Environment, path1, path2 string) bool { // compare the base names! { - same, _ := couldBeSameFile(env, d1, d2) + same, _ := couldBeSameFile(d1, d2) return same } } // couldBeSameFile checks if path1 might be the same as path2. // -// If both files exist, compares using [env.SameFile]. +// If both files exist, compares using [os.SameFile]. // Otherwise compares absolute paths using string comparison. // // same indicates if they might be the same file. // authorative indiciates if the result is authorative. -func couldBeSameFile(env environment.Environment, path1, path2 string) (same, authorative bool) { +func couldBeSameFile(path1, path2 string) (same, authorative bool) { { // stat both files info1, err1 := os.Stat(path1) diff --git a/pkg/fsx/type.go b/pkg/fsx/type.go index 11f8a5e..34d7445 100644 --- a/pkg/fsx/type.go +++ b/pkg/fsx/type.go @@ -4,30 +4,28 @@ package fsx import ( "io/fs" "os" - - "github.com/FAU-CDI/wisski-distillery/pkg/environment" ) // Exists checks if the given path exists -func Exists(env environment.Environment, path string) bool { +func Exists(path string) bool { _, err := os.Lstat(path) return err == nil } // IsDirectory checks if the provided path exists and is a directory -func IsDirectory(env environment.Environment, path string) bool { +func IsDirectory(path string) bool { info, err := os.Stat(path) return err == nil && info.Mode().IsDir() } // IsFile checks if the provided path exists and is a regular file -func IsFile(env environment.Environment, path string) bool { +func IsFile(path string) bool { info, err := os.Stat(path) return err == nil && info.Mode().IsRegular() } // IsLink checks if the provided path exists and is a symlink -func IsLink(env environment.Environment, path string) bool { +func IsLink(path string) bool { info, err := os.Lstat(path) return err == nil && info.Mode()&fs.ModeSymlink != 0 } diff --git a/pkg/fsx/unmasked.go b/pkg/fsx/unmasked.go deleted file mode 100644 index 8602012..0000000 --- a/pkg/fsx/unmasked.go +++ /dev/null @@ -1,101 +0,0 @@ -package fsx - -import ( - "io/fs" - "os" - "sync" - "syscall" - "time" -) - -// mask is the global mask lock -var m mask - -// mask allows disabling and re-enabling the global umask. -// it is used by allow functions of this package. -type mask struct { - l sync.Mutex // locked? - umask int // previous mask -} - -// Lock blocks until no other function is using this umask -// and then sets it to 0. -func (mask *mask) Lock() { - mask.l.Lock() - mask.umask = syscall.Umask(0) -} - -func (mask *mask) Unlock() { - mask.umask = syscall.Umask(mask.umask) - mask.l.Unlock() -} - -// WriteFile is like [os.WriteFile], but ignores the umask. -func WriteFile(path string, data []byte, mode fs.FileMode) error { - handle, err := Create(path, mode) - if err != nil { - return err - } - defer handle.Close() - - if _, err := handle.Write(data); err != nil { - return err - } - - return nil -} - -// Create creates a new file with the given mode. -// This function ignores the umask. -func Create(path string, mode fs.FileMode) (*os.File, error) { - m.Lock() - defer m.Unlock() - - return os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, mode) -} - -// Mkdir creates a new directory with the given mode. -// This function ignores the umask. -func Mkdir(path string, mode fs.FileMode) error { - m.Lock() - defer m.Unlock() - - return os.Mkdir(path, fs.ModeDir|mode) -} - -// MkdirAll creates a new directory and all potentially missing parent directories. -// This function ignores the umask. -func MkdirAll(path string, mode fs.FileMode) error { - m.Lock() - defer m.Unlock() - - return os.MkdirAll(path, fs.ModeDir|mode) -} - -// Touch touches a file. -// It is similar to the unix 'touch' command. -// -// If the file does not exist exists, it is created using [Create]. -// If the file does exist, it's access and modification times are updated to the current time. -// -// This function ignores the umask. -func Touch(path string, perm fs.FileMode) error { - if perm == 0 { - perm = DefaultFilePerm - } - _, err := os.Stat(path) - switch { - case os.IsNotExist(err): - f, err := Create(path, perm) - if err != nil { - return err - } - defer f.Close() - return nil - case err != nil: - return err - default: - now := time.Now().Local() - return os.Chtimes(path, now, now) - } -}