github.com/elves/elvish@v0.15.0/pkg/eval/mods/unix/umask.go (about)

     1  // +build !windows,!plan9,!js
     2  
     3  package unix
     4  
     5  import (
     6  	"fmt"
     7  	"math"
     8  	"strconv"
     9  	"sync"
    10  
    11  	"golang.org/x/sys/unix"
    12  
    13  	"github.com/elves/elvish/pkg/eval/errs"
    14  	"github.com/elves/elvish/pkg/eval/vals"
    15  	"github.com/elves/elvish/pkg/eval/vars"
    16  )
    17  
    18  const (
    19  	validUmaskMsg = "integer in the range [0..0o777]"
    20  )
    21  
    22  //elvdoc:var umask
    23  //
    24  // The file mode creation mask. Its value is a string in Elvish octal
    25  // representation; e.g. 0o027. This makes it possible to use it in any context
    26  // that expects a `$number`.
    27  //
    28  // When assigning a new value a string is implicitly treated as an octal
    29  // number. If that fails the usual rules for interpreting
    30  // [numbers](./language.html#number) are used. The following are equivalent:
    31  // `unix:umask = 027` and `unix:umask = 0o27`. You can also assign to it a
    32  // `float64` data type that has no fractional component.
    33  // The assigned value must be within the range [0 ... 0o777], otherwise the
    34  // assignment will throw an exception.
    35  //
    36  // You can do a temporary assignment to affect a single command; e.g.
    37  // `umask=077 touch a_file`. After the command completes the old umask will be
    38  // restored. **Warning**: Since the umask applies to the entire process, not
    39  // individual threads, changing it temporarily in this manner is dangerous if
    40  // you are doing anything in parallel. Such as via the
    41  // [`peach`](ref/builtin.html#peach) command.
    42  
    43  // UmaskVariable is a variable whose value always reflects the current file
    44  // creation permission mask. Setting it changes the current file creation
    45  // permission mask for the process (not an individual thread).
    46  type UmaskVariable struct{}
    47  
    48  var _ vars.Var = UmaskVariable{}
    49  
    50  // Guard against concurrent fetch and assignment of $unix:umask. This assumes
    51  // no other part of the elvish code base will call unix.Umask() as it only
    52  // protects against races involving the aforementioned Elvish var.
    53  var umaskMutex sync.Mutex
    54  
    55  // Get returns the current file creation umask as a string.
    56  func (UmaskVariable) Get() interface{} {
    57  	// Note: The seemingly redundant syscall is because the unix.Umask() API
    58  	// doesn't allow querying the current value without changing it. So ensure
    59  	// we reinstate the current value.
    60  	umaskMutex.Lock()
    61  	defer umaskMutex.Unlock()
    62  	umask := unix.Umask(0)
    63  	unix.Umask(umask)
    64  	return fmt.Sprintf("0o%03o", umask)
    65  }
    66  
    67  // Set changes the current file creation umask. It can be called with a string
    68  // (the usual case) or a float64.
    69  func (UmaskVariable) Set(v interface{}) error {
    70  	var umask int
    71  
    72  	switch v := v.(type) {
    73  	case string:
    74  		i, err := strconv.ParseInt(v, 8, 0)
    75  		if err != nil {
    76  			i, err = strconv.ParseInt(v, 0, 0)
    77  			if err != nil {
    78  				return errs.BadValue{
    79  					What: "umask", Valid: validUmaskMsg, Actual: vals.ToString(v)}
    80  			}
    81  		}
    82  		umask = int(i)
    83  	case float64:
    84  		intPart, fracPart := math.Modf(v)
    85  		if fracPart != 0 {
    86  			return errs.BadValue{
    87  				What: "umask", Valid: validUmaskMsg, Actual: vals.ToString(v)}
    88  		}
    89  		umask = int(intPart)
    90  	default:
    91  		return errs.BadValue{
    92  			What: "umask", Valid: validUmaskMsg, Actual: vals.ToString(v)}
    93  	}
    94  
    95  	if umask < 0 || umask > 0o777 {
    96  		// TODO: Switch to `%O` when Go 1.15 is the minimum acceptable version.
    97  		// Until then the formatting of negative numbers will be weird.
    98  		return errs.OutOfRange{
    99  			What: "umask", ValidLow: "0", ValidHigh: "0o777",
   100  			Actual: fmt.Sprintf("0o%o", umask)}
   101  	}
   102  
   103  	umaskMutex.Lock()
   104  	defer umaskMutex.Unlock()
   105  	unix.Umask(umask)
   106  	return nil
   107  }