123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108 |
- //go:build !windows && !plan9 && !js
- package unix
- import (
- "fmt"
- "math"
- "strconv"
- "sync"
- "golang.org/x/sys/unix"
- "src.elv.sh/pkg/eval/errs"
- "src.elv.sh/pkg/eval/vals"
- "src.elv.sh/pkg/eval/vars"
- )
- const (
- validUmaskMsg = "integer in the range [0..0o777]"
- )
- //elvdoc:var umask
- //
- // The file mode creation mask. Its value is a string in Elvish octal
- // representation; e.g. 0o027. This makes it possible to use it in any context
- // that expects a `$number`.
- //
- // When assigning a new value a string is implicitly treated as an
- // octal number. If that fails the usual rules for interpreting
- // [numbers](./language.html#number) are used. The following are equivalent:
- // `set unix:umask = 027` and `set unix:umask = 0o27`. You can also assign to it
- // a `float64` data type that has no fractional component. The assigned value
- // must be within the range [0 ... 0o777], otherwise the assignment will throw
- // an exception.
- //
- // You can do a temporary assignment to affect a single command; e.g. `umask=077
- // touch a_file`. After the command completes the old umask will be restored.
- // **Warning**: Since the umask applies to the entire process, not individual
- // threads, changing it temporarily in this manner is dangerous if you are doing
- // anything in parallel, such as via the [`peach`](builtin.html#peach) command.
- // UmaskVariable is a variable whose value always reflects the current file
- // creation permission mask. Setting it changes the current file creation
- // permission mask for the process (not an individual thread).
- type UmaskVariable struct{}
- var _ vars.Var = UmaskVariable{}
- // Guard against concurrent fetch and assignment of $unix:umask. This assumes
- // no other part of the elvish code base will call unix.Umask() as it only
- // protects against races involving the aforementioned Elvish var.
- var umaskMutex sync.Mutex
- // Get returns the current file creation umask as a string.
- func (UmaskVariable) Get() interface{} {
- // Note: The seemingly redundant syscall is because the unix.Umask() API
- // doesn't allow querying the current value without changing it. So ensure
- // we reinstate the current value.
- umaskMutex.Lock()
- defer umaskMutex.Unlock()
- umask := unix.Umask(0)
- unix.Umask(umask)
- return fmt.Sprintf("0o%03o", umask)
- }
- // Set changes the current file creation umask. It can be called with a string
- // (the usual case) or a float64.
- func (UmaskVariable) Set(v interface{}) error {
- var umask int
- switch v := v.(type) {
- case string:
- i, err := strconv.ParseInt(v, 8, 0)
- if err != nil {
- i, err = strconv.ParseInt(v, 0, 0)
- if err != nil {
- return errs.BadValue{
- What: "umask", Valid: validUmaskMsg, Actual: vals.ToString(v)}
- }
- }
- umask = int(i)
- case int:
- // We don't bother supporting big.Int or bit.Rat because no valid umask value would be
- // represented by those types.
- umask = v
- case float64:
- intPart, fracPart := math.Modf(v)
- if fracPart != 0 {
- return errs.BadValue{
- What: "umask", Valid: validUmaskMsg, Actual: vals.ToString(v)}
- }
- umask = int(intPart)
- default:
- return errs.BadValue{
- What: "umask", Valid: validUmaskMsg, Actual: vals.Kind(v)}
- }
- if umask < 0 || umask > 0o777 {
- return errs.OutOfRange{
- What: "umask", ValidLow: "0", ValidHigh: "0o777",
- Actual: fmt.Sprintf("%O", umask)}
- }
- umaskMutex.Lock()
- defer umaskMutex.Unlock()
- unix.Umask(umask)
- return nil
- }
|