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 }