@@ -342,8 +342,42 @@ func (n *overlayNode) locateChild(name string) source {
342342 }
343343}
344344
345+ // goModeToUnix converts Go's os.FileMode to a Unix mode_t (uint32).
346+ // Go uses its own bit layout (e.g. ModeDir = 1<<31) which differs from
347+ // Unix/FUSE mode bits (S_IFDIR = 0040000). This must be used whenever
348+ // populating fuse.Attr.Mode, fuse.DirEntry.Mode, or fs.StableAttr.Mode.
349+ func goModeToUnix (m os.FileMode ) uint32 {
350+ perm := uint32 (m .Perm ())
351+ switch {
352+ case m .IsDir ():
353+ perm |= syscall .S_IFDIR
354+ case m & os .ModeSymlink != 0 :
355+ perm |= syscall .S_IFLNK
356+ case m & os .ModeNamedPipe != 0 :
357+ perm |= syscall .S_IFIFO
358+ case m & os .ModeSocket != 0 :
359+ perm |= syscall .S_IFSOCK
360+ case m & os .ModeCharDevice != 0 :
361+ perm |= syscall .S_IFCHR
362+ case m & os .ModeDevice != 0 :
363+ perm |= syscall .S_IFBLK
364+ default :
365+ perm |= syscall .S_IFREG
366+ }
367+ if m & os .ModeSetuid != 0 {
368+ perm |= syscall .S_ISUID
369+ }
370+ if m & os .ModeSetgid != 0 {
371+ perm |= syscall .S_ISGID
372+ }
373+ if m & os .ModeSticky != 0 {
374+ perm |= syscall .S_ISVTX
375+ }
376+ return perm
377+ }
378+
345379func localAttrToFuse (info os.FileInfo , out * fuse.Attr ) {
346- out .Mode = uint32 (info .Mode ())
380+ out .Mode = goModeToUnix (info .Mode ())
347381 out .Size = uint64 (info .Size ())
348382 out .Mtime = uint64 (info .ModTime ().Unix ())
349383 fillStatFields (info , out )
@@ -352,14 +386,14 @@ func localAttrToFuse(info os.FileInfo, out *fuse.Attr) {
352386func sftpAttrToFuse (info os.FileInfo , out * fuse.Attr ) {
353387 s := info .Sys ()
354388 if stat , ok := s .(* sftp.FileStat ); ok {
355- out .Mode = uint32 (info .Mode ())
389+ out .Mode = goModeToUnix (info .Mode ())
356390 out .Size = uint64 (info .Size ())
357391 out .Mtime = uint64 (stat .Mtime )
358392 out .Atime = uint64 (stat .Atime )
359393 out .Uid = stat .UID
360394 out .Gid = stat .GID
361395 } else {
362- out .Mode = uint32 (info .Mode ())
396+ out .Mode = goModeToUnix (info .Mode ())
363397 out .Size = uint64 (info .Size ())
364398 out .Mtime = uint64 (info .ModTime ().Unix ())
365399 out .Atime = out .Mtime
@@ -387,13 +421,13 @@ func (n *overlayNode) Lookup(ctx context.Context, name string, out *fuse.EntryOu
387421 // Check upper first
388422 if info , err := os .Lstat (up ); err == nil {
389423 localAttrToFuse (info , & out .Attr )
390- stable := fs.StableAttr {Mode : uint32 (info .Mode ())}
424+ stable := fs.StableAttr {Mode : goModeToUnix (info .Mode ())}
391425 return n .NewInode (ctx , child , stable ), fs .OK
392426 }
393427 // Fall back to lower
394428 if info , err := n .sftpLstat (rp ); err == nil {
395429 sftpAttrToFuse (info , & out .Attr )
396- stable := fs.StableAttr {Mode : uint32 (info .Mode ())}
430+ stable := fs.StableAttr {Mode : goModeToUnix (info .Mode ())}
397431 return n .NewInode (ctx , child , stable ), fs .OK
398432 }
399433 return nil , syscall .ENOENT
@@ -413,7 +447,7 @@ func (n *overlayNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno)
413447 seen [e .Name ()] = struct {}{}
414448 result = append (result , fuse.DirEntry {
415449 Name : e .Name (),
416- Mode : uint32 (info .Mode ()),
450+ Mode : goModeToUnix (info .Mode ()),
417451 })
418452 }
419453 }
@@ -431,7 +465,7 @@ func (n *overlayNode) Readdir(ctx context.Context) (fs.DirStream, syscall.Errno)
431465 }
432466 result = append (result , fuse.DirEntry {
433467 Name : e .Name (),
434- Mode : uint32 (e .Mode ()),
468+ Mode : goModeToUnix (e .Mode ()),
435469 })
436470 }
437471 return nil
@@ -563,7 +597,7 @@ func (n *overlayNode) Create(ctx context.Context, name string, flags uint32, mod
563597 localAttrToFuse (info , & out .Attr )
564598
565599 child := n .child (name )
566- stable := fs.StableAttr {Mode : uint32 (info .Mode ())}
600+ stable := fs.StableAttr {Mode : goModeToUnix (info .Mode ())}
567601 inode := n .NewInode (ctx , child , stable )
568602 return inode , & localFileHandle {f : f }, fuse .FOPEN_DIRECT_IO , fs .OK
569603}
@@ -581,7 +615,7 @@ func (n *overlayNode) Mkdir(ctx context.Context, name string, mode uint32, out *
581615 localAttrToFuse (info , & out .Attr )
582616
583617 child := n .child (name )
584- stable := fs.StableAttr {Mode : uint32 (info .Mode ())}
618+ stable := fs.StableAttr {Mode : goModeToUnix (info .Mode ())}
585619 return n .NewInode (ctx , child , stable ), fs .OK
586620}
587621
@@ -684,7 +718,7 @@ func (n *overlayNode) Symlink(ctx context.Context, target, name string, out *fus
684718 localAttrToFuse (info , & out .Attr )
685719
686720 child := n .child (name )
687- stable := fs.StableAttr {Mode : uint32 (info .Mode ())}
721+ stable := fs.StableAttr {Mode : goModeToUnix (info .Mode ())}
688722 return n .NewInode (ctx , child , stable ), fs .OK
689723}
690724
0 commit comments