@@ -403,3 +403,126 @@ func TestThreadSafety(t *testing.T) {
403403 require .NoError (t , err )
404404 assert .Len (t , fi , files * 2 )
405405}
406+
407+ func TestUmask (t * testing.T ) {
408+ tests := []struct {
409+ name string
410+ umask * uint32
411+ expectedFileMode os.FileMode
412+ expectedDirMode os.FileMode
413+ }{
414+ {
415+ name : "default umask (0o022)" ,
416+ umask : nil ,
417+ expectedFileMode : 0o644 ,
418+ expectedDirMode : 0o755 ,
419+ },
420+ {
421+ name : "custom umask (0o077)" ,
422+ umask : func () * uint32 { u := uint32 (0o077 ); return & u }(),
423+ expectedFileMode : 0o600 ,
424+ expectedDirMode : 0o700 ,
425+ },
426+ {
427+ name : "zero umask (0o000)" ,
428+ umask : func () * uint32 { u := uint32 (0o000 ); return & u }(),
429+ expectedFileMode : 0o666 ,
430+ expectedDirMode : 0o777 ,
431+ },
432+ }
433+
434+ for _ , tt := range tests {
435+ t .Run (tt .name , func (t * testing.T ) {
436+ var fs billy.Filesystem
437+ if tt .umask != nil {
438+ fs = New (WithUmask (* tt .umask ))
439+ } else {
440+ fs = New ()
441+ }
442+
443+ f , err := fs .Create ("file.txt" )
444+ require .NoError (t , err )
445+ f .Close ()
446+
447+ if runtime .GOOS == "windows" && tt .umask == nil {
448+ tt .expectedFileMode |= 0o022
449+ tt .expectedDirMode |= 0o022
450+ }
451+
452+ fi , err := fs .Stat ("file.txt" )
453+ require .NoError (t , err )
454+ assert .Equal (t , tt .expectedFileMode , fi .Mode ().Perm ())
455+
456+ err = fs .MkdirAll ("testdir" , 0o777 )
457+ require .NoError (t , err )
458+
459+ fi , err = fs .Stat ("testdir" )
460+ require .NoError (t , err )
461+ assert .Equal (t , tt .expectedDirMode , fi .Mode ().Perm ())
462+ })
463+ }
464+ }
465+
466+ func TestUmaskOpenFile (t * testing.T ) {
467+ fs := New (WithUmask (0o077 ))
468+
469+ f , err := fs .OpenFile ("test.txt" , os .O_CREATE | os .O_RDWR , 0o666 )
470+ require .NoError (t , err )
471+ assert .NoError (t , f .Close ())
472+
473+ fi , err := fs .Stat ("test.txt" )
474+ require .NoError (t , err )
475+ assert .Equal (t , os .FileMode (0o600 ), fi .Mode ().Perm ())
476+
477+ // Re-do test without the use of os.O_CREATE.
478+ err = util .WriteFile (fs , "test2.txt" , []byte ("content" ), 0o666 )
479+ require .NoError (t , err )
480+
481+ fi2 , err := fs .Stat ("test2.txt" )
482+ require .NoError (t , err )
483+ assert .Equal (t , os .FileMode (0o600 ), fi2 .Mode ().Perm ())
484+
485+ f2 , err := fs .OpenFile ("test2.txt" , os .O_RDWR , 0o777 )
486+ require .NoError (t , err )
487+ assert .NoError (t , f2 .Close ())
488+
489+ fi2 , err = fs .Stat ("test2.txt" )
490+ require .NoError (t , err )
491+ // Mode must not be changed by OpenFile without os.O_CREATE.
492+ assert .Equal (t , os .FileMode (0o600 ), fi2 .Mode ().Perm ())
493+ }
494+
495+ func TestUmaskChmod (t * testing.T ) {
496+ fs := New ()
497+
498+ f , err := fs .Create ("/test.txt" )
499+ require .NoError (t , err )
500+ assert .NoError (t , f .Close ())
501+
502+ want := 0o644
503+ if runtime .GOOS == "windows" {
504+ want |= 0o022
505+ }
506+
507+ fi , err := fs .Stat ("/test.txt" )
508+ require .NoError (t , err )
509+ assert .Equal (t , os .FileMode (want ), fi .Mode ().Perm ())
510+
511+ ch , ok := fs .(billy.Chmod )
512+ require .True (t , ok , "fs does not implement billy.Chmod" )
513+
514+ err = ch .Chmod ("/test.txt" , 0o421 )
515+ require .NoError (t , err )
516+
517+ fi , err = fs .Stat ("/test.txt" )
518+ require .NoError (t , err )
519+ assert .Equal (t , os .FileMode (0o421 ), fi .Mode ().Perm ())
520+ }
521+
522+ func TestUmaskRootDirectory (t * testing.T ) {
523+ fs := New (WithUmask (0o077 ))
524+
525+ fi , err := fs .Stat ("/" )
526+ require .NoError (t , err )
527+ assert .Equal (t , os .FileMode (0o700 ), fi .Mode ().Perm ())
528+ }
0 commit comments